Renaming a .Zip entry using only C# / .NET? - c#

So what I'm trying to do right now is opening a .Zip file and renaming a file inside it (.NET 4.6.1). I don't think I'm allowed to use third-party libraries since this is a very simple operation (or so I thought, because I couldn't find any MSDN function to rename entries).
I found a couple of ways, but they are nasty. You can extract the file to disk and add it again with a different name, or you can also create a new entry with the new name in the zip, copy the file through a stream, and delete the original entry.
Is there any effective way to do this? I don't mind any ideas at this point. I know that with DotNetZip its only one line but I can't use a third part library.
Thanks a lot for the help!

Using the ZipArchive in System.IO.Compression. Here is an example that adds a .dat extension to every entry in the specified zip file:
private static void RenameZipEntries(string file)
{
using (var archive = new ZipArchive(File.Open(file, FileMode.Open, FileAccess.ReadWrite), ZipArchiveMode.Update))
{
var entries = archive.Entries.ToArray();
foreach (var entry in entries)
{
var newEntry = archive.CreateEntry(entry.Name + ".dat");
using (var a = entry.Open())
using (var b = newEntry.Open())
a.CopyTo(b);
entry.Delete();
}
}
}

Related

Fast way to add a single file to a large ZIP file using C#

I have a large zip file (let's say 10 GB), to which I want to add a single small file (let's say 50 KB). I'm using the following code:
using System.IO.Compression;
using (var targetZip = ZipFile.Open(largeZipFilePath), ZipArchiveMode.Update)
{
targetZip.CreateEntryFromFile(smallFilePath, "foobar");
}
While this works (eventually), it takes a very long time and consumes a ludicrous amount of memory. It seems to extract and recompress the whole archive.
How can I improve this in .Net 4.7? Solution without external dependencies is preferred, but not required if impossible.
use visual studio nuget package manager and install that
Install-Package DotNetZip -Version 1.11.0
using (ZipFile zip = new ZipFile())
{
zip.AddFile("ReadMe.txt"); // no password for this one
zip.Password= "123456!";
zip.AddFile("7440-N49th.png");
zip.Password= "!Secret1";
zip.AddFile("2005_Annual_Report.pdf");
zip.Save("Backup.zip");
}
https://www.nuget.org/packages/DotNetZip/
Since you are in above .NET 4.5, you can use the ZipArchive (System.IO.Compression) class to achieve this. Here is the MSDN documentation: (MSDN).
Here is their example, it just writes text, but you could read in a .csv file and write it out to your new file. To just copy the file in, you would use CreateFileFromEntry, which is an extension method for ZipArchive.
using (FileStream zipToOpen = new FileStream(#"c:\users\exampleuser\release.zip", FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen, ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry = archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("Information about this package.");
writer.WriteLine("========================");
}
}
}
Check this:- https://stackoverflow.com/a/22339337/9912441
https://learn.microsoft.com/en-us/dotnet/standard/io/how-to-compress-and-extract-files
I found the reason for this behaviour in another Stack Overflow answer: Out of memory exception while updating zip in c#.net.
The gist of it is that this takes a long time because ZipArchiveMode.Update caches the zip file into memory. The suggestion for avoiding this caching behaviour is to create a new archive, and copy the old archive contents along with the new file to it.
See the MSDN documentation which explains how ZipArchiveMode.Update behaves:

c#/WinForms - Take file from resources and place it in directory on C: drive

I'm sure this has been done before and is probably quite simple to achieve but I can't seem to find anything on the internet when I've searched.
I'm looking for a way to pickup/copy a file from my applications resources and place it into a folder on my the C: drive. Can this be done? Or should I read in the contents of the file and then create a new one in the desired directory?Any pointers/advice would be appreciated! Thanks.
You can take the contents of the embedded file and write them out to your desired location as follows:
using (var fs = new FileStream(#"C:\Temp\Foo.txt", FileMode.Create))
using (var sw = new StreamWriter(fs))
{
var data = Stuff.Foo;
sw.Write(data);
}
I did this for a text file embedded in Stuff.resx in my project. There's a Write overload for various types so you can use what you need. For instance, an image will come back as a bitmap.
I achieved this by using File.WriteAllBytes()
Example:
File.WriteAllBytes("C:\\MyApp\\TextExample.json", MyApp.Properties.Resources.TextExample);

Opening Split Zip Files With DotNetZip

I am trying to extract files from zip files using the DotNetZip library. I am able to extract files when it is a single .zip file. However, when I try to extract files from a multi volume zip file like Something.zip.0 or Something.zip.1, I get the following two exceptions:
-Exception thrown: 'Ionic.Zip.BadReadException' in Ionic.Zip.dll
-Exception thrown: 'Ionic.Zip.ZipException' in Ionic.Zip.dll
Is it possible for DotNetZip to read these type of files, or should I be looking into an alternative approach? I am working on Visual Studios using C#.
Here's a snippet of how I implement my zip file extraction.
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(_pathToZip))
{
zip.CompressionLevel = Ionic.Zlib.CompressionLevel.BestSpeed;
foreach(Ionic.Zip.ZipEntry ze in zip)
{
string fileName = ze.FileName;
bool isThereItemToExtract = isThereMatch(fileName.ToLower(), _folderList, _fileList);
if (isThereItemToExtract)
{
string pathOfFileToExtract = (_destinationPath + "\\" + ze.FileName).Replace('/', '\\');
string pathInNewZipFile = goUpOneDirectoryRelative(ze.FileName);
ze.Extract(_destinationPath, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
_newZip.AddItem(pathOfFileToExtract, pathInNewZipFile);
}
}
_newZip.Save();
}
Please refer the DotNetZipLibrary code examples:
using Ionic.Zip;
private void MyExtract(string zipToUnpack, string unpackDirectory)
{
using (ZipFile zip1 = ZipFile.Read(zipToUnpack))
{
// here, we extract every entry, but we could extract conditionally
// based on entry name, size, date, checkbox status, etc.
foreach (ZipEntry e in zip1)
{
e.Extract(unpackDirectory, ExtractExistingFileAction.OverwriteSilently);
}
}
}
This method should be able to extract either split and not split zip files.
Every zip entry will be extracted with its full path as specified in the zip archive, relative to the current unpackDirectory.
There's no need to check if zip entry exsists (isThereItemToExtract). Interating the zip entries with foreach should do the job.
To avoid collisions you need to check if file with same name as zipEntry exsists in the unpackDirectory, or use ExtractExistingFileAction.OverwriteSilently flag.
Is it possible for DotNetZip to read these type of files, or should I be looking into an alternative approach? I am working on Visual Studios using C#.
In my experience, this is the best library to deal with split zip files.

Add zipping capability to existing StreamWriter code

The program I am working on is currently using a StreamWriter to create one or many text files in a target folder. Off StreamWriter class, I am using WriteLine and its IDisposable interface via Using directive (for implicit .Close).
I need to add an option to create one or many text files in a zip archive inside a target folder. I was going to change existing code to use streams, so it's possible to use a ZIP file as an output (planning to use DotNetZip).
I was thinking to create some GetOutputStream function and feed that into the currently existing method. This function would determine whether archive option is set, and either create plain files, or archive them. Problem is that MemoryStream, which looks like a good buffer class to use with DotNetZip, does not intersect with StreamWriter in the inheritance hierarchy.
Looks like my only option is to create some IWriteLine interface, which would implement WriteLine and IDisposable. Then branch two new child classes from StreamWriter and MemoryStream, and implement IWriteLine in them.
Is there a better solution?
The current code conceptually looks like this:
Using sw As StreamWriter = File.CreateText(fullPath)
sw.WriteLine(header)
sw.WriteLine(signature)
While dr.Read 'dr=DataReader
Dim record As String = GetDataRecord(dr)
sw.WriteLine(record)
End While
End Using
For code samples, either VB.NET or C# is fine, although this is more of a conceptual question.
EDIT: Cannot use .NET 4.5's System.IO.Compression.ZipArchive, have to stick with .NET 4.0. We still need to support clients running on Windows 2003.
Use the StreamWriter(Stream) constructor to have it write to a MemoryStream. Set the Position back to 0 so you can then save the written text to the archive with ZipFile.Save(Stream). Check the ZipIntoMemory helper method in the project's sample code for guidance.
First of all, with .NET 4.5 System.IO.Compression.ZipArchive class (see http://msdn.microsoft.com/en-us/library/system.io.compression.ziparchive.aspx) you no longer need DotNetZip at least for common zipping tasks.
It could look like this:
string filePath = "...";
//Create file.
using (FileStream fileStream = File.Create(filePath))
{
//Create archive infrastructure.
using (ZipArchive archive = new ZipArchive(fileStream, ZipArchiveMode.Create, true, Encoding.UTF8))
{
SqlDataReader sqlReader = null;
//Reading each row into a separate text file in the archive.
while(sqlReader.Read())
{
string record = sqlReader.GetString(0);
//Archive entry is a file inside archive.
ZipArchiveEntry entry = archive.CreateEntry("...", CompressionLevel.Optimal);
//Get stream to write the archive item body.
using (Stream entryStream = entry.Open())
{
//All you need here is to write data into archive item stream.
byte[] recordData = Encoding.Unicode.GetBytes(record);
MemoryStream recordStream = new MemoryStream(recordData);
recordStream.CopyTo(entryStream);
//Flush the archive item to avoid data loss on dispose.
entryStream.Flush();
}
}
}
}

C# Access text file in zip archive

How can I read content of a text file inside a zip archive?
For example I have an archive qwe.zip, and insite it there's a file asd.txt, so how can I read contents of that file?
Is it possible to do without extracting the whole archive? Because it need to be done quick, when user clicks a item in a list, to show description of the archive (it needed for plugin system for another program). So extracting a whole archive isn't the best solution... because it might be few Mb, which will take at least few seconds or even more to extract... while only that single file need to be read.
You could use a library such as SharpZipLib or DotNetZip to unzip the file and fetch the contents of individual files contained inside. This operation could be performed in-memory and you don't need to store the files into a temporary folder.
Unzip to a temp-folder take the file and delete the temp-data
public static void Decompress(string outputDirectory, string zipFile)
{
try
{
if (!File.Exists(zipFile))
throw new FileNotFoundException("Zip file not found.", zipFile);
Package zipPackage = ZipPackage.Open(zipFile, FileMode.Open, FileAccess.Read);
foreach (PackagePart part in zipPackage.GetParts())
{
string targetFile = outputDirectory + "\\" + part.Uri.ToString().TrimStart('/');
using (Stream streamSource = part.GetStream(FileMode.Open, FileAccess.Read))
{
using (Stream streamDestination = File.OpenWrite(targetFile))
{
Byte[] arrBuffer = new byte[10000];
int iRead = streamSource.Read(arrBuffer, 0, arrBuffer.Length);
while (iRead > 0)
{
streamDestination.Write(arrBuffer, 0, iRead);
iRead = streamSource.Read(arrBuffer, 0, arrBuffer.Length);
}
}
}
}
}
catch (Exception)
{
throw;
}
}
Although late in the game and the question is already answered, in hope that this still might be useful for others who find this thread, I would like to add another solution.
Just today I encountered a similar problem when I wanted to check the contents of a ZIP file with C#. Other than NewProger I cannot use a third party library and need to stay within the out-of-the-box .NET classes.
You can use the System.IO.Packaging namespace and use the ZipPackage class. If it is not already included in the assembly, you need to add a reference to WindowsBase.dll.
It seems, however, that this class does not always work with every Zip file. Calling GetParts() may return an empty list although in the QuickWatch window you can find a property called _zipArchive that contains the correct contents.
If this is the case for you, you can use Reflection to get the contents of it.
On geissingert.com you can find a blog article ("Getting a list of files from a ZipPackage") that gives a coding example for this.
SharpZipLib or DotNetZip may still need to get/read the whole .zip file to unzip a file. Actually, there is still method could make you just extract special file from the .zip file without reading the entire .zip file but just reading small segment.
I needed to have insights into Excel files, I did it like so:
using (var zip = ZipFile.Open("ExcelWorkbookWithMacros.xlsm", ZipArchiveMode.Update))
{
var entry = zip.GetEntry("xl/_rels/workbook.xml.rels");
if (entry != null)
{
var tempFile = Path.GetTempFileName();
entry.ExtractToFile(tempFile, true);
var content = File.ReadAllText(tempFile);
[...]
}
}

Categories