On one of my sites, I encountered a problem when users where downloading and extracting the contents of ZIP archives created by SharpZipLib. These errors mainly occurred when users were using the Windows Compressed Folders Extraction Wizard (the Windows XP built-in ZIP archive software).
Some points of confusion is that I used 7-Zip and I was able to open and extract all of the files without any problems. however, the default Windows XP Compressed Folder extraction wizard generated errors. Then after fixing some items, everything worked with Firefox, but when MSIE downloaded the file, I received an error and the downloaded file sizes were different than when using Firefox.
After a bit of searching, I came across Liam Westley’s blog that pointed out that Windows has a problem with special characters in the filenames of ZIP files and recommending using ZipEntry.CleanName function.
Some of the error messages that I was seeing include:
- Windows Extraction Wizard Error:
The Compressed (zipped) Folder is invalid or corrupted. - Windows Extraction Wizard Error:
No files to extract. - Windows Security Warning Error:
Windows has blocked access to these files to help protect your computer.
In each instance, the ZIP archive contained files and 7-Zip was able to extract the files. Each of those files were viewable using the corresponding software product (MS Word, Excel, etc.).
The SharpZipLib Frequently Asked Questions recommending using the CleanName (as Liam pointed out) and specifying the file size of the ZipEntry when adding it to the ZIP archive.
Windows XP explorer doesn’t show my files, what’s wrong?
To allow the windows built in Zip handling to show details in a Zip file the entries must use relative paths. A relative path should start with a name like example.txt or subdir/a.dat.The other cause of problems is Zip64 extensions which allow a very large number of files and very large files to be compressed. However XP doesn’t understand Zip64 and will complain or show incorrect file sizes.
Within the library when creating entries try to ensure that the size of entries is set before they are added to the archive. This ensures the correct format is used. If you cant do this then a fallback is the UseZip64 property which can be used to turn this off. The downside is that if a large file is encountered then archive creation will fail.
– SharpZipLib, FAQ
After adding in the ZipEntry.CleanName function (to make sure the names are valid to Windows), setting the ZipEntry.Size attribute to the file size, and setting Zip64 to “off”, I continued to get errors.
After a bit, I noticed that the ZIP archive file sizes when downloaded using Mozilla Firefox differed from when downloaded using Microsoft Internet Explorer. This confused me even more, since the ZIP functions are executing on the server and which browser is downloading the ZIP file shouldn’t affect the server-side code. I also noticed that the Firefox downloads worked with Windows Compressed Folder Extraction Wizard and the Internet Explorer downloads returned a error of “No files to extract.”
After digging around a bit a thought popped into my head. I thought that somehow the Response stream was being modified when using MSIE during the server-side execution of creating the ZIP file. That would explain why the file sizes were different between MSIE and Firefox ZIP archive files. So I moved around my code statements a little so that I call all of the Response methods after I have created the ZIP file.
Before code (worked in Firefox, but not in MSIE):
Response.Buffer = true; Response.Clear(); Response.ClearContent(); Response.ClearHeaders(); Response.ContentType = "application/zip"; Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.zip", downloadFilename)); // CREATE ZIP FILE CODE HERE Response.WriteFile(zipFileName); Response.Flush(); Response.End();
After code (worked in both):
// CREATE ZIP FILE CODE HERE Response.Buffer = true; Response.Clear(); Response.ClearContent(); Response.ClearHeaders(); Response.ContentType = "application/zip"; Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.zip", downloadFilename)); Response.WriteFile(zipFileName); Response.Flush(); Response.End();
To my surprise, the above code changes had zero impact on the problem on the web server (while continuing to work fine on localhost). MSIE continued to download corrupt ZIP files while Firefox continued to download valid ZIP files.
Another thought was the mime-type settings on the server… I thought that might be different between my localhost and the web server. The server mime-type for .zip extension was “application/x-zip-compressed”, while the mime-type on my localhost and used in the above code was “application/zip.” So I tried modifying the code to use “application/unknown” as the mime-type to allow the client to determine the correct mime-type.
After changing the code to use “application/unknown”, the file size when downloaded using MSIE matched the file size when downloaded using Firefox. The MSIE downloaded ZIP files also began to work properly with all ZIP software. I’ve had similar issues before with IIS and mime-types and every time it was the last thing that came to my mind.
In the end, I made five major changes. Although, I believe that only two of them were causing my errors that were resolved by using CleanName and changing the mime-type assignment. The items are as follows:
- Use ZipEntry.CleanName when adding entry to ZipStream
- Set the entry’s size property
- Turn off Zip64 support
- Change mime-type to application/unknown
- Restructuring the code to group Response methods
Hi,
I am still having problems to extract the zip file. Can you please check what I am missing:
The error is “The file is corrupted”.
protected void DownloadSelectedFiles_Click(object sender, EventArgs e)
{
string path = String.Empty;
string zipFilePath = @”C:\MyZipFile.zip”;
ZipOutputStream zipStream = new ZipOutputStream(File.Create(zipFilePath));
byte[] buffer = new byte[4096];
foreach (GridViewRow row in gvFiles.Rows)
{
bool isSelected = (row.FindControl(”chkSelect”) as CheckBox).Checked;
if (isSelected)
{
path = (row.FindControl(”lblUrl”) as Label).Text;
ZipEntry entry = new ZipEntry(Path.GetFileName(path));
entry.DateTime = DateTime.Now;
ZipEntry.CleanName(entry.Name);
zipStream.PutNextEntry(entry);
using (FileStream fs = File.OpenRead(path))
{
int sourceBytes;
do
{
sourceBytes = fs.Read(buffer, 0, buffer.Length);
zipStream.Write(buffer, 0, sourceBytes);
} while (sourceBytes > 0);
}
}
}
double zipLength = (double) zipStream.Length;
zipStream.Finish();
zipStream.Close();
string fileName = Path.GetFileName(zipFilePath);
Response.Buffer = true;
Response.Clear();
Response.ClearContent();
Response.ClearHeaders();
Response.ContentType = “application/unknown”;
Response.AppendHeader(”Content-Disposition”, “attachment; filename=SS.zip”);
Response.WriteFile(zipFilePath);
Response.Flush();
Response.Close();
Response.End();
}
Thanks,
Azam
First, check the file with a few different ZIP programs to make sure the file isn’t corrupt. If the file is really corrupt, then there might be something wrong with your code somewhere.
Next, check your server’s mime type setting for .zip files, then try using that instead of application/unknown or try using “application/x-zip-compressed”.
Finally, try turning off Zip64 compatibility.
Hi Jim,
I had a similar problem where I got the ‘corrupted’ message in IE, but it worked fine in firefox. I’m using django/python on the server, but I was having the same problem as you.
After hours of searching for a solution and trying different things, your trick of changing mime-type to application/unknown (instead of application/zip) worked!!! I have no idea why that works, perhaps there’s a better solution, but at this point – I don’t really care ;-)
Thanks for your post!