OutputStreamed files not valid for Windows Explorer

Nov 24, 2009 at 4:17 PM

This code offers a ZIP file for download that can then not be opened by Windows Explorer (but by 7zip).

Does anyone see anything wrong with this code?

    protected void Page_Load(object sender, EventArgs e)
    {
        var fileInfos = new List<string> { 
        @"C:\Temp\sub1\file1.pdf", @"C:\Temp\sub1\file2.gz"
        };
        streamZipToClient2(Response, "test.zip", "myzip", @"C:\Temp\", fileInfos);
    }


    static void streamZipToClient2(HttpResponse response, string zipFileName, string zipFileBaseDirectory, string physicalBasePath, IEnumerable<string> physicalFilePaths)
    {        
        response.Clear();
        response.Buffer = false;

        long approximateZipFileSize = 0;

        foreach (var filePath in physicalFilePaths)
        {
            approximateZipFileSize += (new FileInfo(filePath)).Length;
        }

        response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
        response.Cache.SetNoStore();
        response.Cache.SetExpires(DateTime.UtcNow.AddYears(-1));

        response.ContentType = "application/zip";
        response.AddHeader("content-disposition", "filename=" + zipFileName);   
        response.AddHeader("content-length", (approximateZipFileSize * 1.2).ToString());
        
        using (var zipFile = new Ionic.Zip.ZipFile())
        {
            zipFile.CompressionLevel = Ionic.Zlib.CompressionLevel.None;            
            foreach (var physicalFilePath in physicalFilePaths)
            {
                var fileBytes = File.ReadAllBytes(physicalFilePath);

                string relativeFilePath = physicalFilePath.Replace(physicalBasePath, "");                
                string fileName = Path.GetFileName(physicalFilePath);
                string directoryInZipFile = Path.Combine(zipFileBaseDirectory, relativeFilePath.Replace(fileName, ""));                
                zipFile.AddEntry(fileName, directoryInZipFile, fileBytes);                
                zipFile.Save(response.OutputStream);                
            }
        }
        HttpContext.Current.ApplicationInstance.CompleteRequest();
    }

The content length and caching code is not responsible (omitting it doesn't help).
All the files exist (and the resulting structure of the zip file is fine, as evidenced by 7zips handling).

I tried this code with the current 1.8 release and the current 1.9-dev release (with some differing semantics).

Any help would be much appreciated!

Thanks

Nov 24, 2009 at 4:30 PM

Moving the call to Save(...) out of the loop solved this problem.

Coordinator
Nov 24, 2009 at 6:33 PM

That sounds right.  If you keep the Save() in the loop, then it saves multiple independent ZIPs, one after the other, to the output stream, and I'm sure the results would be unpredictable in that case.

Also, think about Response.Close() instead of HttpContext.Current.ApplicationInstance.CompleteRequest();

Coordinator
Dec 5, 2009 at 12:01 PM

ps, I think you should use Response.Close() instead of HttpContext.Current.ApplicationInstance.CompleteRequest();

Dec 5, 2009 at 2:07 PM

Yes, a call to Response.Close() is necessary for the streaming to work if the size is not given correctly.