Issues with HttpResponseStream

Nov 24, 2009 at 6:43 PM
Edited Nov 24, 2009 at 6:46 PM

Executing the code below gives me a

 NotSupportedException: Specified Method is not supported.

Stacktrace:

   at System.Web.HttpResponseStream.get_Position()
   at Ionic.Zip.ZipEntry.WriteHeader(Stream s, Int32 cycle)
   at Ionic.Zip.ZipOutputStream._InitiateCurrentEntry(Boolean finishing)
   at Ionic.Zip.ZipOutputStream.Write(Byte[] buffer, Int32 offset, Int32 count)
   at PageTest.streamZipToClient2(HttpResponse response, String zipFileName, String zipFileBaseDirectory, String physicalBasePath, IEnumerable`1 physicalFilePaths) in c:\Entwicklung\Projekte\PoolarServer\ClientName\trunk\PageTest.aspx.cs:line 74

I need to do it this way because I check for Response.IsClientConnected while streaming in the data (some file are >1GB).
A compromise would be to just check after each entry addition, but I couldn't find a way to do that either (ZipOutputStream has no Write(Stream stream). And a ZipFile would not begin streaming before calling save.

Any tips here?

Many thanks!

Code:

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

        HttpContext.Current.ApplicationInstance.CompleteRequest();
    }


    static bool 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 zipStream = new Ionic.Zip.ZipOutputStream(response.OutputStream))
        {
                zipStream.CompressionLevel = Ionic.Zlib.CompressionLevel.None;
                int bufferSize = 1024 * 1024;
                var buffer = new byte[bufferSize];
                foreach (var physicalFilePath in physicalFilePaths)
                {
                    using (var fileStream = File.OpenRead(physicalFilePath))
                    {
                        string relativeFilePath = physicalFilePath.Replace(physicalBasePath, "");
                        string fileName = Path.GetFileName(physicalFilePath);
                        string directoryInZipFile = Path.Combine(zipFileBaseDirectory, relativeFilePath.Replace(fileName, ""));
                        string entryName = Path.Combine(directoryInZipFile, fileName);

                        zipStream.PutNextEntry(entryName);

                        long remaining = fileStream.Length;
                        while (remaining > 0)
                        {
                            if (!response.IsClientConnected)
                            {
                                return false;
                            }
                            int read = fileStream.Read(buffer, 0, bufferSize);
                            remaining -= read;

                            zipStream.Write(buffer, 0, read);                           
                        }
                    }
                }
        }
        return true;
    }
Coordinator
Nov 24, 2009 at 8:04 PM

looks like a bug to me. . .

I'll need to look closer, and get back to you.

 

Nov 24, 2009 at 11:44 PM

That would be very nice, thank you.

As soon as I've integrated the zipping code into the rest of the application, there will be some testing of the streamed archives on Mac computers. I will feed back the results (as there still seems to be some uncertainty about that issue)

Coordinator
Nov 25, 2009 at 6:23 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Coordinator
Dec 5, 2009 at 1:01 PM

ps: I think you should use Response.Close() instead of

HttpContext.Current.ApplicationInstance.CompleteRequest();

Dec 18, 2009 at 9:21 AM

I needed to do exactly the same as above and got the same exception.

I encounter it with Response buffer set to false. The internal stream usage needs to know it stream position, but there is no code for wrapping the write-only stream in a CountingStream.

So, I've fixed the bug by changing _Init() in Ionic.Zip.ZipOutputStream as follows:

        private void _Init(Stream stream, bool leaveOpen)
        {
            _outputStream = stream.CanRead ? stream : new CountingStream(stream);
CompressionLevel = Ionic.Zlib.CompressionLevel.Default; _encryption = EncryptionAlgorithm.None; _entriesWritten = new List<ZipEntry>(); _zip64 = Zip64Option.Never; _leaveUnderlyingStreamOpen = leaveOpen; Strategy = Ionic.Zlib.CompressionStrategy.Default; _name = "unknown"; #if !NETCF ParallelDeflateThreshold = -1L; #endif }
I hope this helps someone else, and that this fix (or possibly a "better" solution makes it into the official release as soon as possible (so I don't have to keep a separate branch myself...) :)
Coordinator
Dec 18, 2009 at 4:34 PM
Edited Dec 18, 2009 at 4:39 PM

Aquamoth, thanks, that's helpful. 

The fix will be in vv1.9.0.33.