Tuesday, December 2, 2008

Windows XP Unzip Errors With SharpZipLib

Great Utility

SharpZipLib is a great little library that helps you programmatically compress/decompress zip archives. If your needs aren't too exotic (ie. you need to programmatically zip/unzip a series of files/folders) this could very well be your ticket.

You've probably heard of it before (it's been around for quite a while), some sample usage might look like this (compresses a file).

using (ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipFilePath)))
{
//Compression level 0-9 (9 is highest)
zipStream.SetLevel(GetCompressionLevel());

//Add an entry to our zip file
ZipEntry entry = new ZipEntry(Path.GetFileName(sourceFilePath));
entry.DateTime = DateTime.Now;
zipStream.PutNextEntry(entry);

byte[] buffer = new byte[4096];
int byteCount = 0;

using (FileStream inputStream = File.OpenRead(sourceFilePath))
{
byteCount = inputStream.Read(buffer, 0, buffer.Length);
while (byteCount > 0)
{
zipStream.Write(buffer, 0, byteCount);
byteCount = inputStream.Read(buffer, 0, buffer.Length);
}
}
}

This is pretty normal usage. It adds a zip entry to an archive and creates said archive. What might confuse you though is the error you'll get if you try to to unpack the archive using a legacy unzip tool (ie. the stock Windows XP decompression tool).

The stock Windows XP extraction tool won't be able to unpack the archive and will complain about the archive being "invalid or corrupted" if you try to extract the archive contents.

The Compressed (zipped) Folder is invalid or corrupted.

Zip64 Extensions

What's happening here is that the utility is enabling Zip64 extensions for the archive, and some older utilities can't read Zip64 Extensions. You could simply turn off Zip64 Extensions, but this will cause problems when you start adding files larger than 4GB to your archive.

A better solution is to make a mild tweak to the way we add files to the archive. By specifying the size of the file we're adding, the ZipOutputStream can decide whether or not to use the Zip64 extensions. If we don't need them then they'll be turned off automatically. The mild tweak below fixes the error from the above code:

...
ZipEntry entry = new ZipEntry(Path.GetFileName(sourceFilePath));
entry.DateTime = DateTime.Now;
/* By specifying a size, SharpZipLib will turn on/off UseZip64 based on the file sizes. If Zip64 is ON
* some legacy zip utilities (ie. Windows XP) who can't read Zip64 will be unable to unpack the archive.
* If Zip64 is OFF, zip archives will be unable to support files larger than 4GB. */
entry.Size = new FileInfo(sourceFilePath).Length;
zipStream.PutNextEntry(entry);
...

Hope that helps someone. That error drove me crazy for a while and I didn't find much via Google.

Best,
Tyler

17 comments:

Anonymous said...

The timing of this post was great. I had a web app using the SharpZipLib code and had no issues using my current Winzip or 7-zip.

Users with Winzip 8.0, Freezip, and the XP Compressed Folders were encountering problems. What I noticed was if I opened the old zip file with XP Compressed Folders, it would display both the packed size and original size as 4Gb which certainly coincides with the Zip64 comment.

Anonymous said...

Thank You for this post!

Rich said...

Excellent post, thanks for the info!

Anonymous said...

Very nice, would have taken me forever to fix this error. Thanks!

Anonymous said...

Thank you.
This is also the workaround for the error if you add an empty file (0 bytes) via SharpZipLib 0.85.5 to an ZIP archive. WinZip 10.0 consider this files as invalid: "Error: invalid compressed data to inflate".

Thank you for this post.
Tom

qhnguyen said...

Still not correct. This is a bug in SharpZipLib

Anonymous said...

This solved my problem! I was wondering myself why it wouldn't open with the XP compressed folders. I have WinZip installed and it wasn't a problem.

Thanks!

Anonymous said...

Thanks, your comment was very useful.

Anonymous said...

That works for me:
private void ZipFiles(string sActivityType, bool bPrintedOnly)
{
List filesNames_Guid = GenerateFilesList(sActivityType, bPrintedOnly);

Guid filename = Guid.NewGuid();

FileStream ostream;
byte[] obuffer;
//generic and unique name for the zipped file, needs to be removed afterwards
string outPath = Server.MapPath("~/TMP") + @"\" + filename.ToString() + ".zip";
ZipOutputStream oZipStream = new ZipOutputStream(System.IO.File.Create(outPath)); // create zip stream
//oZipStream.UseZip64 = UseZip64.Off;
ZipEntry oZipEntry;

for (int i = 0; i < filesNames_Guid.Count - 1; i = i + 2)
{
oZipEntry = new ZipEntry(filesNames_Guid[i]);

// start of the fix.
FileInfo MyFileInfo = new FileInfo(Server.MapPath("~/ArtifactLibrary") + "\\" + filesNames_Guid[i + 1]);
oZipEntry.DateTime = MyFileInfo.CreationTime;
oZipEntry.Size = MyFileInfo.Length;
// end of the fix.
oZipStream.PutNextEntry(oZipEntry);
ostream = System.IO.File.OpenRead(Server.MapPath("~/ArtifactLibrary") + "\\" + filesNames_Guid[i + 1]);
obuffer = new byte[ostream.Length];
ostream.Read(obuffer, 0, obuffer.Length);
oZipStream.Write(obuffer, 0, obuffer.Length);
ostream.Close();
}
oZipStream.Finish();

HttpContext.Current.Response.AddHeader("Content-Disposition", ("attachment; filename=" + ViewState["Name_Selected"] + " " + sActivityType + (bPrintedOnly ? " PrintedOnly" : "") + ".zip"));
HttpContext.Current.Response.AddHeader("Content-Length", oZipStream.Length.ToString());
HttpContext.Current.Response.ContentType = "application/octet-stream";

oZipStream.Close();

HttpContext.Current.Response.TransmitFile(outPath);

}

Daniel said...

You rock, completely solved my problem. I am zipping in C#, unzipping in Java, which was error'ing out with "invalid entry size (expected 4294967295 but got xxx bytes)" before your fix.

Thank you!

wolf354 said...

Thanks ... because you helped a lot, but a more correct answer to this kind of problem is to simply turn zip64 off, like this:
zipOutput.UseZip64 = UseZip64.Off;

Anonymous said...

How is that a "solution"?

Use the 7-zip option to avoid having Zip64 used on <4gb files... which XP doesn't have a problem with anyway.

If the file is >4gb, Windows can't open them anyway... with Zip64 or not.

Doesn't that mean it won't work either way?

Bad Monkeh said...

Thank is exactly what i was looking for. Thanks a lot.

Ihsan said...

I was searching for a work-around, and saw this post. It looks like this is a fix to help people avoid FUTURE problems--could you possibly help me with a fix for something at hand?

I'm trying to unzip a password protected archive containing two video files, 486mb and 286mb. When I go to extract files/open archive, and put in password, I receive this message: "Error: invalid compressed data to inflate."

I need a workaround to rescue and recover my files. Is this possible?

Anonymous said...

@Ihsan: As a workaround to extract zip files that have this problem, you can copy them to a Windows7 machine. There it is possible to open the ZIP archive.

Anonymous said...

Thanks for the solution. Solved my problem.. :)

Anonymous said...

Real helpful! Thanks.