[
  {
    "Id": "554101",
    "ThreadId": "242980",
    "Html": "\r\n<p>I'm wondering what&nbsp;the diferences are when calling&nbsp; ZipFile.Save on a non seekable stream vs. seekable stream.</p>\r\n<p>I'm working on an simple asp.net service that dynamical creates and streams a zip archive.&nbsp;&nbsp; The zip archive itself works fine&nbsp;under windows the files don't seem corrupted, and the zip file seems&nbsp;fine,&nbsp;etc.&nbsp; However when trying\r\n to read the archive from another program as an application package I ran into issues.&nbsp; The file was just not reading properly.</p>\r\n<p>I've spent hours trying to figure out what the issue was.&nbsp; Initially I used ZipOutputStream and then ZipFile,&nbsp; but it just came down to there&nbsp;being a slight difference in the zip archive created by DoNetZip&nbsp;that was causing it to fail.</p>\r\n<p>I finally figured out by outputing to a memory stream first and then sending it to Response.OutputStream completely fixed the problem.</p>\r\n<p>So this works:</p>\r\n<div style=\"color:black; background-color:white\">\r\n<pre><span style=\"color:blue\">var</span> ms = <span style=\"color:blue\">new</span> System.IO.MemoryStream();\r\n\r\n<span style=\"color:blue\">using</span> (ZipFile zip = <span style=\"color:blue\">new</span> ZipFile())\r\n{\r\n    <span style=\"color:green\">//Add some stuff to the archive.</span>\r\n    zip.Save(ms);\r\n}\r\n\r\nms.Position = 0;\r\nms.CopyTo(Response.OutputStream);\r\nResponse.Flush();\r\nHttpContext.Current.ApplicationInstance.CompleteRequest();\r\n</pre>\r\n</div>\r\n<p>While this does not:</p>\r\n<div style=\"color:black; background-color:white\">\r\n<pre><span style=\"color:blue\">using</span> (ZipFile zip = <span style=\"color:blue\">new</span> ZipFile())\r\n{\r\n    <span style=\"color:green\">//Add some stuff to the archive.</span>\r\n    zip.Save(Response.OutputStream);\r\n}\r\n\r\nResponse.Flush();\r\nHttpContext.Current.ApplicationInstance.CompleteRequest();\r\n</pre>\r\n</div>\r\n<p>Obviously it's not ideal to load everything in memory, but it seems that there is is something going on when the stream is seekable that&nbsp;doesn't happen when it isn't.&nbsp; So I'm wondering what the difference is and if there is something I can do to\r\n the output stream to fix it.</p>\r\n<p>I know there is a work item open to expose CountingStream as a public type:&nbsp;<a href=\"http://dotnetzip.codeplex.com/workitem/12374\">http://dotnetzip.codeplex.com/workitem/12374</a></p>\r\n<p>Although I'm not sure if that solves my particular problem or not.</p>\r\n",
    "PostedDate": "2011-01-23T17:11:39.69-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "558117",
    "ThreadId": "242980",
    "Html": "\r\n<p>Two things.</p>\r\n<p>I think the call to ....CompleteRequest() might be incorrect.&nbsp; You may be more correct in calling Response.Close() after Response.Flush().&nbsp; Also, Response.End() is an option, but be aware that Response.End() suspends execution of your page at that\r\n point. If you have code or logic that follows Response.End(), it will not be executed.&nbsp; In that case you need to use Response.Close().&nbsp;&nbsp; The ....CompleteRequest() was something that I put into various samples a long time ago, but since then\r\n I've learned that it results in incorrect behavior some of the time.&nbsp; so I'd recommend against it and if you see an example that still shows ...CompleteRequest(), either in the official DotNetZip documentation or on some website somewhere, let me know\r\n and I will try to get it changed.</p>\r\n<p>Ok, independent of that, you ask an interesting question: what's the difference between saving to a seekable vs non-seekable stream.&nbsp; The original zip format had the unfortunate problem that the entry header provided information about the length of\r\n the compressed entry.&nbsp; An app or library that produces a zip file (like DotNetZip) needed to know the length of the compressed stream, before it could emit the header information into the zip file.&nbsp; But you can't know the length of the compressed\r\n stream until you do the compression, and the header information must appear before the compressed stream in the zip archive.&nbsp; To address the ordering issue, Most apps and libraries just emitted dummy data for the header, then wrote the compressed stream,\r\n then did a seek back to the dummy data, and overwrote with valid header data.&nbsp; This works fine, but obviously the technique requires a seekable stream.</p>\r\n<p>When producing a zip tool to do piped output, let's say when you want to zip something and then uuencode the result of the zip, using | on the OS command line, seeking back is not possible. So PKWare updated the zip format to allow the length of the compressed\r\n stream for&nbsp;each entry to be encoded *after* the compressed stream in the zip archive.&nbsp; This encoding&nbsp;is an option to the &quot;traditional&quot; encoding, and it is signaled in a bitfield in the zipfile using the 3rd bit - hence it is sometimes called\r\n &quot;bit 3 encoding&quot;.&nbsp;&nbsp; Most zip files don't use bit 3 encoding, but some do.&nbsp;&nbsp;It works fine, but ... not all zip libraries and applications support this modification of the zip format.&nbsp; This change, by the way, was made in ... I can't\r\n remember, but I think it was 1994 or prior.&nbsp; So this is not a recent thing.&nbsp; Regardless, some modern tools still don't support bit 3 encoding.&nbsp; In particular, I understand that the built in archive tool in current versions of Mac OS does not\r\n suppport reading or writing zips that use bit 3 encoding.&nbsp;</p>\r\n<p>DotNetZip supports both the original encoding and bit 3 encoding, but uses bit 3 only when the zipfile is written to a non-seekable stream, in order to maximize interoperability.&nbsp;</p>\r\n<p>If you are producing a zip file on a web server, it's tempting to just write it out to Response.OutputStream for download to the browser machine. But this isn't foolproof because of limitations on some&nbsp;operating systems and tools.&nbsp; If you're sure\r\n all clients will be Windows clients, it is no problem, because Windows can read bit-3 encoded zip files without a problem (since at least Windows 2000, and maybe earlier).&nbsp; But if your client list will include other operating systems, then bit-3 encoded\r\n zip files may introduce problems.</p>\r\n<p>I hope that clarifies things.</p>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2011-01-31T07:21:50.307-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "558411",
    "ThreadId": "242980",
    "Html": "\r\n<p>Thanks for the detailed response.</p>\r\n<p>Actually I was going back and forth between using Response.End() and CompleteRequest() based on the discussions on StackOverflow.&nbsp; I'm not sure what is better&nbsp;at this point :-)&nbsp; But I guess Response.Close/End is probably more clear if it works\r\n just as well.</p>\r\n<p>What I'm doing is&nbsp;creating a silverlight xap on the fly, so apparently silverlight also requires this length information in the header.&nbsp;</p>\r\n<p>Do you think&nbsp;there would be a way to write this length information manually to the zip stream in order to use the original encoding?&nbsp; What if you compressed each file beforehand and could determine the compressed length of each entry?&nbsp; Do\r\n you think this information could be written to the header before compressing the files to the output stream?&nbsp; I suppose I would then have to keep dotnetzip from writing the info at the end of the stream.</p>\r\n",
    "PostedDate": "2011-01-31T17:51:13.88-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "558473",
    "ThreadId": "242980",
    "Html": "\r\n<p>The best way to avoid bit-3 encoding is to use a seekable stream.&nbsp; A MemoryStream would work, as would a FileStream.&nbsp;</p>\r\n<p>I think it would be impractical to save to Response.OutputStream, and then somehow doctor up the zip file and move bits around to eliminate the bit-3 encoding.&nbsp; It's possible to do, but when you write to Response.OutputStream, the data is GONE after\r\n you;ve written it.&nbsp; So there's no chance for your app to intercept and modify it.&nbsp; If you *could* do that, why not just use a seekable stream, like a MemoryStream, which would give you the same result?</p>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2011-01-31T21:33:06.09-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "558490",
    "ThreadId": "242980",
    "Html": "\r\n<p>Ideally I would be compressing the stream on the server while the stream is being downloaded to the client so the browser loads the&nbsp;silverlight&nbsp;application&nbsp;as quickly as possible.</p>\r\n<p>The idea would be to create a non-seekable wrapper stream for Response.OutputStream that I would give to DotNetZip that could write it's own header while ignoring any header bytes (and trailing headers?) being sent by DotNetZip.</p>\r\n<p>However now that I think about it that's probably just me over engineering as usual ;-) &nbsp;I think in the end my Xap files aren't going to be nearly as large as what I'm testing with (so it&nbsp;exaggerates&nbsp;the latency) and most of the time the xap\r\n will be cached on the server as well. &nbsp;So more than likely it's not an issue. &nbsp;At least nothing I need to worry about right now.</p>\r\n<p>But thanks for the help and feedback.</p>\r\n",
    "PostedDate": "2011-01-31T22:23:34.76-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "559384",
    "ThreadId": "242980",
    "Html": "\r\n<p>Hmm - if you are relying on an application-layer cache, then it could be simple to, for the first request when the cache is empty,&nbsp;just write the XAP file to the cache location on disk (or even into a MemoryStream), and then stream the contents of that\r\n cache location to Response.OutputStream.&nbsp; In this case you have a seekable stream for the initial DotNetZip output - either a filestream or a MemoryStream - and so you avoid the bit-3 encoding problem.&nbsp;</p>\r\n<p>If you are relying on the kernel-mode cache, then I would say, do the same thing, but after streaming the content to Response.OutputStream, clear the temp location of the zip file.</p>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2011-02-02T06:52:09.813-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "561629",
    "ThreadId": "242980",
    "Html": "\r\n<p>Hi Cheeso,</p>\r\n<p>Just to chime in a bit here on the HttpApplication<strong>.</strong>CompleteRequest vs Response.End vs Response.Close issue...</p>\r\n<p>From what I've read, Response.Close sends a connection-reset packet to the client brower which tells the browser to stop receiving any more data on that connection. As a result, if the last few bits of the zip file get delayed&nbsp;while travelling across\r\n the network&nbsp;and the reset arrives at the browser first it means that the downloaded zip file will get truncated. There was a post here (<a href=\"http://dotnetzip.codeplex.com/workitem/12466\">http://dotnetzip.codeplex.com/workitem/12466</a>) a while ago\r\n which was fixed by switching from Response.Close to using HttpApplication.CompleteRequest for some transatlantic downloads that were affected by this.</p>\r\n<p>Response.End seems to be a bit more friendly, but basically&nbsp;terminates the ASP.NET request at that point so any clean-up code after it won't actually get executed. I don't know if that applies to &quot;finally&quot; blocks or not, or how it deals with flushing\r\n any pending buffers.</p>\r\n<p>Overall HttpApplication.CompleteRequest appears to be the cleanest and most controlled way to end the download request, but I&nbsp;could have missed something important.</p>\r\n<p>There's some links here&nbsp;in&nbsp;the thread <a href=\"http://dotnetzip.codeplex.com/Thread/View.aspx?ThreadId=238541\">\r\nhttp://dotnetzip.codeplex.com/Thread/View.aspx?ThreadId=238541</a>&nbsp;that reference some other articles I found that contain some stuff I found useful.</p>\r\n<p>Hope this helps,</p>\r\n<p>Mike</p>\r\n",
    "PostedDate": "2011-02-06T14:45:41.53-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "566027",
    "ThreadId": "242980",
    "Html": "<p>Thanks, pointyMike, that's useful.&nbsp;</p>",
    "PostedDate": "2011-02-14T11:26:07.63-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  }
]