Creating Directories in a ZipArchive C# .Net 4.5 - c#
A ZipArchive is a collection of ZipArchiveEntries, and adding/removing "Entries" works nicely.
But it appears there is no notion of directories / nested "Archives". In theory, the class is decoupled from a file system, in that you can create the archive completely in a memory stream. But if you wish to add a directory structure within the archive, you must prefix the entry name with a path.
Question: How would you go about extending ZipArchive to create a better interface for creating and managing directories?
For example, the current method of adding a file to a directory is to create the entry with the directory path:
var entry = _archive.CreateEntry("directory/entryname");
whereas something along these lines seems nicer to me:
var directory = _archive.CreateDirectoryEntry("directory");
var entry = _directory.CreateEntry("entryname");
You can use something like the following, in other words, create the directory structure by hand:
using (var fs = new FileStream("1.zip", FileMode.Create))
using (var zip = new ZipArchive(fs, ZipArchiveMode.Create))
{
zip.CreateEntry("12/3/"); // just end with "/"
}
I know I'm late to the party (7.25.2018),
this works flawlessly to me, even with recursive directories.
Firstly, remember to install the NuGet package:
Install-Package System.IO.Compression
And then, Extension file for ZipArchive:
public static class ZipArchiveExtension {
public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName = "") {
var fileName = Path.GetFileName(sourceName);
if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory)) {
archive.CreateEntryFromDirectory(sourceName, Path.Combine(entryName, fileName));
} else {
archive.CreateEntryFromFile(sourceName, Path.Combine(entryName, fileName), CompressionLevel.Fastest);
}
}
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName = "") {
string[] files = Directory.GetFiles(sourceDirName).Concat(Directory.GetDirectories(sourceDirName)).ToArray();
archive.CreateEntry(Path.Combine(entryName, Path.GetFileName(sourceDirName)));
foreach (var file in files) {
archive.CreateEntryFromAny(file, entryName);
}
}
}
And then you can pack anything, whether it is file or directory:
using (var memoryStream = new MemoryStream()) {
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) {
archive.CreateEntryFromAny(sourcePath);
}
}
If you are working on a project that can use full .NET you may try to use
the ZipFile.CreateFromDirectory method, as explained here:
using System;
using System.IO;
using System.IO.Compression;
namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
string startPath = #"c:\example\start";
string zipPath = #"c:\example\result.zip";
string extractPath = #"c:\example\extract";
ZipFile.CreateFromDirectory(startPath, zipPath, CompressionLevel.Fastest, true);
ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}
}
Of course this will only work if you are creating new Zips based on a given directory.
As per the comment, the previous solution does not preserve the directory structure. If that is needed, then the following code might address that:
var InputDirectory = #"c:\example\start";
var OutputFilename = #"c:\example\result.zip";
using (Stream zipStream = new FileStream(Path.GetFullPath(OutputFilename), FileMode.Create, FileAccess.Write))
using (ZipArchive archive = new ZipArchive(zipStream, ZipArchiveMode.Create))
{
foreach(var filePath in System.IO.Directory.GetFiles(InputDirectory,"*.*",System.IO.SearchOption.AllDirectories))
{
var relativePath = filePath.Replace(InputDirectory,string.Empty);
using (Stream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
using (Stream fileStreamInZip = archive.CreateEntry(relativePath).Open())
fileStream.CopyTo(fileStreamInZip);
}
}
Here is one possible solution:
public static class ZipArchiveExtension
{
public static ZipArchiveDirectory CreateDirectory(this ZipArchive #this, string directoryPath)
{
return new ZipArchiveDirectory(#this, directoryPath);
}
}
public class ZipArchiveDirectory
{
private readonly string _directory;
private ZipArchive _archive;
internal ZipArchiveDirectory(ZipArchive archive, string directory)
{
_archive = archive;
_directory = directory;
}
public ZipArchive Archive { get{return _archive;}}
public ZipArchiveEntry CreateEntry(string entry)
{
return _archive.CreateEntry(_directory + "/" + entry);
}
public ZipArchiveEntry CreateEntry(string entry, CompressionLevel compressionLevel)
{
return _archive.CreateEntry(_directory + "/" + entry, compressionLevel);
}
}
and used:
var directory = _archive.CreateDirectory(context);
var entry = directory.CreateEntry(context);
var stream = entry.Open();
but I can foresee problems with nesting, perhaps.
I was also looking for a similar solution and found #Val & #sDima's solution more promising to me. But I found some issues with the code and fixed them to use with my code.
Like #sDima, I also decided to use Extension to add more functionality to ZipArchive.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Compression;
using System.IO;
namespace ZipTest
{
public static class ZipArchiveExtensions
{
public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName, CompressionLevel compressionLevel = CompressionLevel.Optimal)
{
try
{
if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory))
{
archive.CreateEntryFromDirectory(sourceName, entryName, compressionLevel);
}
else
{
archive.CreateEntryFromFile(sourceName, entryName, compressionLevel);
}
}
catch
{
throw;
}
}
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName, CompressionLevel compressionLevel)
{
try
{
var files = Directory.EnumerateFileSystemEntries(sourceDirName);
if (files.Any())
{
foreach (var file in files)
{
var fileName = Path.GetFileName(file);
archive.CreateEntryFromAny(file, Path.Combine(entryName, fileName), compressionLevel);
}
}
else
{
//Do a folder entry check.
if (!string.IsNullOrEmpty(entryName) && entryName[entryName.Length - 1] != '/')
{
entryName += "/";
}
archive.CreateEntry(entryName, compressionLevel);
}
}
catch
{
throw;
}
}
}
}
You can try the extension using the simple given below,
class Program
{
static void Main(string[] args)
{
string filePath = #"C:\Users\WinUser\Downloads\Test.zip";
string dirName = Path.GetDirectoryName(filePath);
if (File.Exists(filePath))
File.Delete(filePath);
using (ZipArchive archive = ZipFile.Open(filePath, ZipArchiveMode.Create))
{
archive.CreateEntryFromFile( #"C:\Users\WinUser\Downloads\file1.jpg", "SomeFolder/file1.jpg", CompressionLevel.Optimal);
archive.CreateEntryFromDirectory(#"C:\Users\WinUser\Downloads\MyDocs", "OfficeDocs", CompressionLevel.Optimal);
archive.CreateEntryFromAny(#"C:\Users\WinUser\Downloads\EmptyFolder", "EmptyFolder", CompressionLevel.Optimal);
};
using (ZipArchive zip = ZipFile.OpenRead(filePath))
{
string dirExtract = #"C:\Users\WinUser\Downloads\Temp";
if (Directory.Exists(dirExtract))
{
Directory.Delete(dirExtract, true);
}
zip.ExtractToDirectory(dirExtract);
}
}
}
I tried to keep the exact behavior of standard CreateEntryFromFilefrom the .Net Framework for extension methods.
To use the sample code given, add a reference to System.IO.Compression.dll and System.IO.Compression.FileSystem.dll.
Following are the advantages of this extension class
Recursive adding of folder content.
Support for an empty folder.
My answer is based on the Val's answer, but a little improved for performance and without producing empty files in ZIP.
public static class ZipArchiveExtensions
{
public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName = "")
{
var fileName = Path.GetFileName(sourceName);
if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory))
{
archive.CreateEntryFromDirectory(sourceName, Path.Combine(entryName, fileName));
}
else
{
archive.CreateEntryFromFile(sourceName, Path.Combine(entryName, fileName), CompressionLevel.Optimal);
}
}
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName = "")
{
var files = Directory.EnumerateFileSystemEntries(sourceDirName);
foreach (var file in files)
{
archive.CreateEntryFromAny(file, entryName);
}
}
}
Example of using:
// Create and open a new ZIP file
using (var zip = ZipFile.Open(ZipPath, ZipArchiveMode.Create))
{
foreach (string file in FILES_LIST)
{
// Add the entry for each file
zip.CreateEntryFromAny(file);
}
}
One more tuned ZipArchive extension, which adds folder structure including all subfolders and files to zip. Solved IOException (Process can't access the file...) which is thrown if files are in use at zipping moment, for example by some logger
public static class ZipArchiveExtensions
{
public static void AddDirectory(this ZipArchive #this, string path)
{
#this.AddDirectory(path, string.Empty);
}
private static void AddDirectory(this ZipArchive #this, string path, string relativePath)
{
var fileSystemEntries = Directory.EnumerateFileSystemEntries(path);
foreach (var fileSystemEntry in fileSystemEntries)
{
if (File.GetAttributes(fileSystemEntry).HasFlag(FileAttributes.Directory))
{
#this.AddDirectory(fileSystemEntry, Path.Combine(relativePath, Path.GetFileName(fileSystemEntry)));
continue;
}
var fileEntry = #this.CreateEntry(Path.Combine(relativePath, Path.GetFileName(fileSystemEntry)));
using (var zipStream = fileEntry.Open())
using (var fileStream = new FileStream(fileSystemEntry, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (var memoryStream = new MemoryStream())
{
fileStream.CopyTo(memoryStream);
var bytes = memoryStream.ToArray();
zipStream.Write(bytes, 0, bytes.Length);
}
}
}
}
A little change in the very good approach from #Andrey
public static void CreateEntryFromDirectory2(this ZipArchive archive, string sourceDirName, CompressionLevel compressionLevel = CompressionLevel.Fastest)
{
var folders = new Stack<string>();
folders.Push(sourceDirName);
do
{
var currentFolder = folders.Pop();
foreach (var item in Directory.GetFiles(currentFolder))
{
archive.CreateEntryFromFile(item, item.Substring(sourceDirName.Length + 1), compressionLevel);
}
foreach (var item in Directory.GetDirectories(currentFolder))
{
folders.Push(item);
}
}
while (folders.Count > 0);
}
Use the recursive approach to Zip Folders with Subfolders.
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
public static async Task<bool> ZipFileHelper(IFolder folderForZipping, IFolder folderForZipFile, string zipFileName)
{
if (folderForZipping == null || folderForZipFile == null
|| string.IsNullOrEmpty(zipFileName))
{
throw new ArgumentException("Invalid argument...");
}
IFile zipFile = await folderForZipFile.CreateFileAsync(zipFileName, CreationCollisionOption.ReplaceExisting);
// Create zip archive to access compressed files in memory stream
using (MemoryStream zipStream = new MemoryStream())
{
using (ZipArchive zip = new ZipArchive(zipStream, ZipArchiveMode.Create, true))
{
await ZipSubFolders(folderForZipping, zip, "");
}
zipStream.Position = 0;
using (Stream s = await zipFile.OpenAsync(FileAccess.ReadAndWrite))
{
zipStream.CopyTo(s);
}
}
return true;
}
//Create zip file entry for folder and subfolders("sub/1.txt")
private static async Task ZipSubFolders(IFolder folder, ZipArchive zip, string dir)
{
if (folder == null || zip == null)
return;
var files = await folder.GetFilesAsync();
var en = files.GetEnumerator();
while (en.MoveNext())
{
var file = en.Current;
var entry = zip.CreateEntryFromFile(file.Path, dir + file.Name);
}
var folders = await folder.GetFoldersAsync();
var fEn = folders.GetEnumerator();
while (fEn.MoveNext())
{
await ZipSubFolders(fEn.Current, zip, dir + fEn.Current.Name + "/");
}
}
I don't like recursion as it was proposed by #Val, #sDima, #Nitheesh. Potentially it leads to the StackOverflowException because the stack has limited size. So here is my two cents with tree traversal.
public static class ZipArchiveExtensions
{
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, CompressionLevel compressionLevel = CompressionLevel.Fastest)
{
var folders = new Stack<string>();
folders.Push(sourceDirName);
do
{
var currentFolder = folders.Pop();
Directory.GetFiles(currentFolder).ForEach(f => archive.CreateEntryFromFile(f, f.Substring(sourceDirName.Length+1), compressionLevel));
Directory.GetDirectories(currentFolder).ForEach(d => folders.Push(d));
} while (folders.Count > 0);
}
}
It's working for me.
Static class
public static class ZipArchiveExtension
{
public static void CreateEntryFromAny(this ZipArchive archive, string sourceName, string entryName = "")
{
var fileName = Path.GetFileName(sourceName);
if (File.GetAttributes(sourceName).HasFlag(FileAttributes.Directory))
{
archive.CreateEntryFromDirectory(sourceName, Path.Combine(entryName, fileName));
}
else
{
archive.CreateEntryFromFile(sourceName, Path.Combine(entryName, fileName), CompressionLevel.Fastest);
}
}
public static void CreateEntryFromDirectory(this ZipArchive archive, string sourceDirName, string entryName = "")
{
string[] files = Directory.GetFiles(sourceDirName).Concat(Directory.GetDirectories(sourceDirName)).ToArray();
if (files.Any())
{
foreach (var file in files)
{
archive.CreateEntryFromAny(file, entryName);
}
}
else
{
if (!string.IsNullOrEmpty(entryName) && entryName[entryName.Length - 1] != '/')
{
entryName += "\\";
}
archive.CreateEntry(entryName);
}
}
}
Calling the method like that
byte[] archiveFile;
using (var memoryStream = new MemoryStream())
{
using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true))
{
archive.CreateEntryFromAny(file.Path);
}
archiveFile = memoryStream.ToArray();
}
Related
How to Rename Files and Folder in .rar .7z, .tar, .zip using C#
I have a compressed file .rar .7z, .tar and .zip and I want to rename physical file name available in above compressed archived using C#. I have tried this using a sharpcompress library but I can't find such a feature for rename file or folder name within .rar .7z, .tar and .zip file. I also have tried using the DotNetZip library but its only support.Zip see what I have tried using DotNetZip library. private static void RenameZipEntries(string file) { try { int renameCount = 0; using (ZipFile zip2 = ZipFile.Read(file)) { foreach (ZipEntry e in zip2.ToList()) { if (!e.IsDirectory) { if (e.FileName.EndsWith(".txt")) { var newname = e.FileName.Split('.')[0] + "_new." + e.FileName.Split('.')[1]; e.FileName = newname; e.Comment = "renamed"; zip2.Save(); renameCount++; } } } zip2.Comment = String.Format("This archive has been modified. {0} files have been renamed.", renameCount); zip2.Save(); } } catch (Exception ex) { MessageBox.Show(ex.ToString()); } } But actually the same as above I also want for .7z, .rar and .tar, I tried many libraries but still I didn't get any accurate solution. Please help me.
This is a simple console application to rename files in .zip using System; using System.IO; using System.IO.Compression; using System.Linq; using System.Text; namespace Renamer { class Program { static void Main(string[] args) { using var archive = new ZipArchive(File.Open(#"<Your File>.zip", FileMode.Open, FileAccess.ReadWrite), ZipArchiveMode.Update); var entries = archive.Entries.ToArray(); //foreach (ZipArchiveEntry entry in entries) //{ // //If ZipArchiveEntry is a directory it will have its FullName property ending with "/" (e.g. "some_dir/") // //and its Name property will be empty string (""). // if (!string.IsNullOrEmpty(entry.Name)) // { // var newEntry = archive.CreateEntry($"{entry.FullName.Replace(entry.Name, $"{RandomString(10, false)}{Path.GetExtension(entry.Name)}")}"); // using (var a = entry.Open()) // using (var b = newEntry.Open()) // a.CopyTo(b); // entry.Delete(); // } //} Parallel.ForEach(entries, entry => { //If ZipArchiveEntry is a directory it will have its FullName property ending with "/" (e.g. "some_dir/") //and its Name property will be empty string (""). if (!string.IsNullOrEmpty(entry.Name)) { ZipArchiveEntry newEntry = archive.CreateEntry($"{entry.FullName.Replace(entry.Name, $"{RandomString(10, false)}{Path.GetExtension(entry.Name)}")}"); using (var a = entry.Open()) using (var b = newEntry.Open()) a.CopyTo(b); entry.Delete(); } }); } //To Generate random name for the file public static string RandomString(int size, bool lowerCase) { StringBuilder builder = new StringBuilder(); Random random = new Random(); char ch; for (int i = 0; i < size; i++) { ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65))); builder.Append(ch); } if (lowerCase) return builder.ToString().ToLower(); return builder.ToString(); } } }
Consider 7zipsharp: https://www.nuget.org/packages/SevenZipSharp.Net45/ 7zip itself supports lots of archive formats (I believe all you mentioned) and 7zipsharp uses the real 7zip. I've used 7zipsharp for .7z files only but I bet it works for others. Here's a sample of a test that appears to rename a file using ModifyArchive method, I suggest you go to school in it: https://github.com/squid-box/SevenZipSharp/blob/f2bee350e997b0f4b1258dff520f36409198f006/SevenZip.Tests/SevenZipCompressorTests.cs Here's the code simplified a bit. Note that the test compresses a 7z file for its test; that's immaterial it could be .txt, etc. Also note it finds the file by index in the dictionary passed to ModifyArchive. Consult documentation for how to get that index from a filename (maybe you have to loop and compare). var compressor = new SevenZipCompressor( ... snip ...); compressor.CompressFiles("tmp.7z", #"Testdata\7z_LZMA2.7z"); compressor.ModifyArchive("tmp.7z", new Dictionary<int, string> { { 0, "renamed.7z" }}); using (var extractor = new SevenZipExtractor("tmp.7z")) { Assert.AreEqual(1, extractor.FilesCount); extractor.ExtractArchive(OutputDirectory); } Assert.IsTrue(File.Exists(Path.Combine(OutputDirectory, "renamed.7z"))); Assert.IsFalse(File.Exists(Path.Combine(OutputDirectory, "7z_LZMA2.7z")));
How to compress both files and folders with ZipFile API
I want to compress Zip to be same structure with folder. but, ZipFile API seems unable to compress folder. How to compress these folder structure? The following code can't compress folder itself. using (ZipArchive archive = ZipFile.Open(Path.Combine(m_strWorkingDirectory, "build.zip"), ZipArchiveMode.Create)) { foreach( string path in m_listTargetPath ) { string strPath = Path.Combine(m_strWorkingDirectory, path); archive.CreateEntryFromFile(strPath, path); } }
If ZipFile.CreateFromDirectory() doesn't do what you need (I thought it would) then you can just get all the files/folders you need and add them in using an extension method: public static class FileExtensions { public static IEnumerable<FileSystemInfo> AllFilesAndFolders(this DirectoryInfo dir) { foreach (var f in dir.GetFiles()) { yield return f; } foreach (var d in dir.GetDirectories()) { yield return d; foreach (var o in AllFilesAndFolders(d)) { yield return o; } } } } And then using your same format, you should be able to do something like so (not tested): DirectoryInfo dir = new DirectoryInfo(m_strWorkingDirectory); using (ZipArchive archive = ZipFile.Open(Path.Combine(m_strWorkingDirectory, "build.zip"), ZipArchiveMode.Create)) { foreach (FileInfo file in dir.AllFilesAndFolders().Where(o => o is FileInfo).Cast<FileInfo>()) { var relPath = file.FullName.Substring(dir.FullName.Length + 1); archive.CreateEntryFromFile(file.FullName, relPath); } }
C# System.IO.IOException
I have following code: using System; using System.Collections.Generic; using System.IO; using VirusTotalNET; using VirusTotalNET.Objects; using System.Linq; using System.Security.Permissions; namespace VirusTotalNETClient { class Program { private const string ScanUrl = "http://www.google.com/"; static void Main(string[] args) { VirusTotal virusTotal = new VirusTotal("5d8684f50946c2bdeaf5c4fd966f61f3661de808e9d7324b99788d6f4fb7ad57"); //Use HTTPS instead of HTTP virusTotal.UseTLS = true; //creating folder for programs reliqies and output log string folderName = "C:\\OnlineScanner"; System.IO.Directory.CreateDirectory(folderName); //get list of files to analyse var paths = Traverse("C:\test"); File.WriteAllLines("C:\\OnlineScanner\\test.txt", paths); foreach (string line in File.ReadLines("C:\\test.txt")) { //Define what file you want to analyse FileInfo fileInfo = new FileInfo(line); //Check if the file has been scanned before. FileReport fileReport = virusTotal.GetFileReport(fileInfo); bool hasFileBeenScannedBefore = fileReport.ResponseCode == ReportResponseCode.Present; //If the file has been scanned before, the results are embedded inside the report. if (hasFileBeenScannedBefore) { int detekce = fileReport.Positives; if (detekce >= 1) { using (var writer = new StreamWriter("C:\\OnlineScanner\\OnlineScannerLog.txt")) { writer.WriteLine(line); writer.WriteLine("URL to test: " + fileReport.Permalink); writer.WriteLine("Detect ratio: " + fileReport.Positives + "/54"); writer.WriteLine("Message: " + fileReport.VerboseMsg); writer.WriteLine(); writer.WriteLine(); } } System.Threading.Thread.Sleep(16000); } else { ScanResult fileResult = virusTotal.ScanFile(fileInfo); int detekce = fileReport.Positives; if (detekce >= 1) { using (var writer = new StreamWriter("C:\\OnlineScanner\\OnlineScannerLog.txt")) { writer.WriteLine(line); writer.WriteLine("URL to test: " + fileReport.Permalink); writer.WriteLine("Detect ratio: " + fileReport.Positives + "/54"); writer.WriteLine("Message: " + fileReport.VerboseMsg); writer.WriteLine(); writer.WriteLine(); } } System.Threading.Thread.Sleep(16000); } } } private static IEnumerable<string> Traverse(string rootDirectory) { IEnumerable<string> files = Enumerable.Empty<string>(); IEnumerable<string> directories = Enumerable.Empty<string>(); try { // The test for UnauthorizedAccessException. var permission = new FileIOPermission(FileIOPermissionAccess.PathDiscovery, rootDirectory); permission.Demand(); files = Directory.GetFiles(rootDirectory); directories = Directory.GetDirectories(rootDirectory); } catch { // Ignore folder (access denied). rootDirectory = null; } foreach (var file in files) { yield return file; } // Recursive call for SelectMany. var subdirectoryItems = directories.SelectMany(Traverse); foreach (var result in subdirectoryItems) { yield return result; } } } } This code run some time (arround 15secs) but then program crashs. The error is System.IO.IOException, process can't access to file C:\hiberfil.sys. http://upnisito.cz/images/2016_12/319crasherrror.png Do you have any idea how to solve it?
create zip for more than one file and then upload it into folder
i want to save zip file to server for more than one file only. first it should create zip file and then upload it to folder if (FileUpload1.HasFile) { string fileName = Path.GetFileName(FileUpload1.PostedFile.FileName); string fileLocation = Server.MapPath("~/uploads/" + fileName); FileUpload1.SaveAs(fileLocation); ZipFile createZipFile = new ZipFile(); createZipFile.AddFile(fileLocation, string.Empty); createZipFile.Save(Server.MapPath("~/uploads/CsharpAspNetArticles.zip")); }
If I understood your "noquestion" question :) You should use IHttpHandler like this: using System; using System.IO; using System.Web; using ICSharpCode.SharpZipLib.Zip; public class ZipHandler : IHttpHandler { public bool IsReusable { get { return true; } } public void ProcessRequest(HttpContext ctx) { var path = HttpUtility.UrlDecode(ctx.Request.QueryString["folder"]); if (path == null) { return; } var folderName = Path.GetFileName(path); if (folderName == String.Empty) { folderName = "root"; } int folderOffset = ctx.Server.MapPath("~").Length; using (var zipStream = new ZipOutputStream(ctx.Response.OutputStream)) { ctx.Response.Clear(); ctx.Response.BufferOutput = false; ctx.Response.AddHeader("Content-Disposition", "attachment; filename=" + folderName + ".zip"); ctx.Response.AddHeader("Content-Type", "application/zip"); zipStream.SetLevel(3); CompressFolder(path, zipStream, folderOffset); ctx.Response.Flush(); } } private static void CompressFolder(string path, ZipOutputStream zipStream, int folderOffset) { string[] files = Directory.GetFiles(path); foreach (string filename in files) { try { using (var streamReader = File.OpenRead(filename)) { var fi = new FileInfo(filename); string entryName = filename.Substring(folderOffset); entryName = ZipEntry.CleanName(entryName); var newEntry = new ZipEntry(entryName) { DateTime = fi.LastWriteTime, Size = fi.Length }; zipStream.PutNextEntry(newEntry); streamReader.CopyTo(zipStream, 4096); zipStream.CloseEntry(); } } catch (IOException) { } } var folders = Directory.GetDirectories(path); foreach (string folder in folders) { CompressFolder(folder, zipStream, folderOffset); } } }
Open a file and replace strings in C#
I'm trying to figure out the best way to open an existing file and replace all strings that match a declared string with a new string, save it then close. Suggestions ?
Can be done in one line: File.WriteAllText("Path", Regex.Replace(File.ReadAllText("Path"), "[Pattern]", "Replacement"));
If you're reading large files in, and your string for replacement may not appear broken across multiple lines, I'd suggest something like the following... private static void ReplaceTextInFile(string originalFile, string outputFile, string searchTerm, string replaceTerm) { string tempLineValue; using (FileStream inputStream = File.OpenRead(originalFile) ) { using (StreamReader inputReader = new StreamReader(inputStream)) { using (StreamWriter outputWriter = File.AppendText(outputFile)) { while(null != (tempLineValue = inputReader.ReadLine())) { outputWriter.WriteLine(tempLineValue.Replace(searchTerm,replaceTerm)); } } } } } Then you'd have to remove the original file, and rename the new one to the original name, but that's trivial - as is adding some basic error checking into the method. Of course, if your replacement text could be across two or more lines, you'd have to do a little more work, but I'll leave that to you to figure out. :)
using System; using System.IO; using System.Text.RegularExpressions; public static void ReplaceInFile( string filePath, string searchText, string replaceText ) { var content = string.Empty; using (StreamReader reader = new StreamReader( filePath )) { content = reader.ReadToEnd(); reader.Close(); } content = Regex.Replace( content, searchText, replaceText ); using (StreamWriter writer = new StreamWriter( filePath )) { writer.Write( content ); writer.Close(); } }
Slight improvement on the accepted answer that doesn't require Regex, and which meets the requirements of the question: File.WriteAllText("Path", File.ReadAllText("Path").Replace("SearchString", "Replacement"));
public partial class ReadAndChange : System.Web.UI.Page { ArrayList FolderList = new ArrayList(); ArrayList FolderListSearch = new ArrayList(); ArrayList FileList = new ArrayList(); protected void Page_Load(object sender, EventArgs e) { AllFolderList("D:\\BinodBackup\\Nilesh\\14.5.2013\\Source"); foreach (string Path in FolderList) { AllFileList(Path); } foreach (string Path in FileList) { ReplaceFile(Path, Path.Replace("Source", "EditedCode")); } //string SourcePath = "D:\\BinodBackup\\Nilesh\\14.5.2013\\Onesource\\Onesource\\UserManagement\\UserControls\\AddUserDetails.ascx.cs"; //string ReplacePath = "D:\\AddUserDetails.ascx.cs"; //ReplaceFile(SourcePath, ReplacePath); } private static void ReplaceFile(string SourcePath, string ReplacePath) { int counter = 1; string line; // Read the file and display it line by line. System.IO.StreamReader file = new System.IO.StreamReader(SourcePath); while ((line = file.ReadLine()) != null) { if (!(line.Contains("//"))) { if (line.Contains(".LogException(")) { //Console.WriteLine(counter.ToString() + ": " + line); string[] arr = line.Split(','); string stringToReplace = arr[0].Replace("LogException", "Publish") + " , " + arr[2].Trim() + " , " + arr[3].Replace(");", "").Trim() + " , " + arr[1].Trim() + ");"; //File.WriteAllText(currentPath, Regex.Replace(File.ReadAllText(currentPath), line, line + " Added")); File.WriteAllText(ReplacePath, File.ReadAllText(ReplacePath).Replace(line, stringToReplace)); //ReplaceInFile(currentPath, line, stringToReplace); } } counter++; } file.Close(); } private void AllFileList(string FolderPath) { DirectoryInfo dir = new DirectoryInfo(FolderPath); DirectoryInfo[] subdir = dir.GetDirectories(); if (subdir.Length > 0) { foreach (DirectoryInfo dr in subdir) { FileInfo[] files1 = dr.GetFiles(); foreach (FileInfo file in files1) { if(file.Name.EndsWith(".cs")) CheckAndAdd((file.DirectoryName + "\\" + file.Name), FileList); } } } } private void AllFolderList(string FolderPath) { string CurFolderPatgh = FolderPath; Again: AddToArrayList(CurFolderPatgh); DirectoryInfo dir = new DirectoryInfo(CurFolderPatgh); DirectoryInfo[] subdir = dir.GetDirectories(); if (subdir.Length > 0) { foreach (DirectoryInfo dr in subdir) { AddToArrayList(((System.IO.FileSystemInfo)(dir)).FullName + "\\" + dr.Name); } } if (FolderListSearch.Count > 0) { foreach (string dr in FolderListSearch) { CurFolderPatgh = dr; FolderListSearch.Remove(dr); goto Again; } } } private void AddToArrayList(string FolderPath) { if (!(FolderList.Contains(FolderPath))) { CheckAndAdd(FolderPath, FolderList); CheckAndAdd(FolderPath, FolderListSearch); } } private void CheckAndAdd(string FolderPath,ArrayList ar) { if (!(ar.Contains(FolderPath))) { ar.Add(FolderPath); } } public static void ReplaceInFile( string filePath, string searchText, string replaceText) { var content = string.Empty; using (StreamReader reader = new StreamReader(filePath)) { content = reader.ReadToEnd(); reader.Close(); } content = content.Replace(searchText, replaceText); using (StreamWriter writer = new StreamWriter(filePath)) { writer.Write(content); writer.Close(); } } }
using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; namespace DevExpressFileEditing { class Program { static List<FileInfo> _files; private static Dictionary<string, string> _replaceList; static void Main() { _files = new List<FileInfo>(); _replaceList = new Dictionary<string, string>(); Console.WriteLine("Dark directory searching"); SearchFilesInDirectories(new DirectoryInfo(#"C:\Sourcebank\Dark")); Console.WriteLine("Light directory searching"); SearchFilesInDirectories(new DirectoryInfo(#"C:\Sourcebank\Light")); Console.WriteLine("{0} files found", _files.Count.ToString(CultureInfo.InvariantCulture)); Console.WriteLine("Replace dictinary creating"); CreateReplaceList(); Console.WriteLine("{0} item added", _replaceList.Count.ToString(CultureInfo.InvariantCulture)); Console.Write("Replacement doing"); for (int i = 0; i < _files.Count; i++) { var fileInfo = _files[i]; Console.CursorLeft = 0; Console.Write("{0} of {1}", i.ToString(CultureInfo.InvariantCulture), _files.Count.ToString(CultureInfo.InvariantCulture)); ReplaceInFile(fileInfo.FullName); } Console.CursorLeft = 0; Console.Write("Replacement done"); } private static void SearchFilesInDirectories(DirectoryInfo dir) { if (!dir.Exists) return; foreach (DirectoryInfo subDirInfo in dir.GetDirectories()) SearchFilesInDirectories(subDirInfo); foreach (var fileInfo in dir.GetFiles()) _files.Add(fileInfo); } private static void CreateReplaceList() { _replaceList.Add("Color=\"#FFF78A09\"", "Color=\"{DynamicResource AccentColor}\""); _replaceList.Add("Color=\"{StaticResource ColorHot}\"", "Color=\"{DynamicResource AccentColor}\""); _replaceList.Add("Color=\"#FFCC0000\"", "Color=\"{DynamicResource AccentColor}\""); _replaceList.Add("To=\"#FFCC0000\"", "To=\"{DynamicResource AccentColor}\""); _replaceList.Add("To=\"#FFF78A09\"", "To=\"{DynamicResource AccentColor}\""); _replaceList.Add("Background=\"#FFF78A09\"", "Background=\"{DynamicResource Accent}\""); _replaceList.Add("Foreground=\"#FFF78A09\"", "Foreground=\"{DynamicResource Accent}\""); _replaceList.Add("BorderBrush=\"#FFF78A09\"", "BorderBrush=\"{DynamicResource Accent}\""); _replaceList.Add("Value=\"#FFF78A09\"", "Value=\"{DynamicResource Accent}\""); _replaceList.Add("Fill=\"#FFF78A09\"", "Fill=\"{DynamicResource Accent}\""); } public static void ReplaceInFile(string filePath) { string content; using (var reader = new StreamReader(filePath)) { content = reader.ReadToEnd(); reader.Close(); } content = _replaceList.Aggregate(content, (current, item) => current.Replace(item.Key, item.Value)); using (var writer = new StreamWriter(filePath)) { writer.Write(content); writer.Close(); } } } }