[
  {
    "Id": "561678",
    "ThreadId": "244895",
    "Html": "\r\n<p>Okay I have been over and over this all weekend and this darn thing just won't work. Here's all the code that I think matters.</p>\r\n<p>&nbsp;</p>\r\n<p>What happens is that I get the zip file, but the file is totally empty. It has the filenames, but they are 0 bytes. Why be this?</p>\r\n<p>&nbsp;</p>\r\n<div style=\"color:black; background-color:white\">\r\n<pre>        <span style=\"color:gray\">///</span> <span style=\"color:gray\">&lt;summary&gt;</span>\r\n        <span style=\"color:gray\">///</span><span style=\"color:green\"> Outputs the zip file stream to the response output stream</span>\r\n        <span style=\"color:gray\">///</span> <span style=\"color:gray\">&lt;/summary&gt;</span>\r\n        <span style=\"color:gray\">///</span> <span style=\"color:gray\">&lt;param name=&quot;docIDs&quot;&gt;</span><span style=\"color:green\">An array of document id's to include in the zip file.&lt;/param&gt;</span>\r\n        <span style=\"color:blue\">private</span> <span style=\"color:blue\">void</span> SendZipResponse(<span style=\"color:blue\">int</span>[] docIDs)\r\n        {\r\n            SPSite site = SPContext.Current.Site;\r\n            Response.Clear();\r\n            <span style=\"color:green\">//set response parameters for the zip attachment</span>\r\n            Response.Buffer = <span style=\"color:blue\">false</span>;\r\n            Response.Expires = 0;\r\n            Response.ContentType = <span style=\"color:#a31515\">&quot;application/x-zip-compressed&quot;</span>;\r\n            String guid =Guid.NewGuid().ToString();\r\n            <span style=\"color:blue\">string</span> disHeader = <span style=\"color:blue\">string</span>.Format(<span style=\"color:#a31515\">&quot;Attachment;Filename=\\&quot;download-{0}.zip\\&quot;&quot;</span>, guid);\r\n            Response.AppendHeader(<span style=\"color:#a31515\">&quot;Content-Disposition&quot;</span>, disHeader);\r\n\r\n            <span style=\"color:green\">//create a lock as the zip output stream is not thread safe</span>\r\n            <span style=\"color:blue\">lock</span> (lockObj)\r\n            {\r\n                <span style=\"color:blue\">try</span>{\r\n                    <span style=\"color:blue\">using</span> (ZipFile zip = <span style=\"color:blue\">new</span> ZipFile())\r\n                    {\r\n                        hs = <span style=\"color:blue\">new</span> Hashtable(docIDs.Length);\r\n\r\n                        <span style=\"color:blue\">foreach</span> (<span style=\"color:blue\">int</span> docID <span style=\"color:blue\">in</span> docIDs)\r\n                        {\r\n                            <span style=\"color:green\">//get a managed file for the binary file data </span>\r\n                            <span style=\"color:green\">//and metadata</span>\r\n                            ManagedFile mf = <span style=\"color:blue\">new</span> ManagedFile(site);\r\n                            mf.ID = docID;\r\n                            mf.Load();\r\n                            <span style=\"color:green\">//get a bytes array from the document                                                 </span>\r\n\r\n                            hs.Add(Path.GetFileName(mf.URL), mf);\r\n                            zip.AddEntry(Path.GetFileName(mf.URL), WriteEntry);\r\n                                          \r\n                        }\r\n                        zip.Save(Response.OutputStream);\r\n                        Response.OutputStream.Flush();\r\n                    }\r\n                }\r\n                <span style=\"color:blue\">catch</span> (Exception ex){\r\n                        Console.Out.Write(<span style=\"color:#a31515\">&quot;storp&quot;</span>);\r\n                    }\r\n         \r\n            Response.Close();\r\n\r\n            }\r\n        }\r\n\r\n        <span style=\"color:blue\">private</span> <span style=\"color:blue\">void</span> WriteEntry(String filename, Stream output)\r\n        {\r\n            ManagedFile value = (ManagedFile)hs[filename];\r\n            output = <span style=\"color:blue\">new</span> MemoryStream(value.GetBinary());\r\n\r\n        }\r\n</pre>\r\n</div>\r\n<p>&nbsp;</p>\r\n<p>and then in the ManagedFile class:</p>\r\n<p>&nbsp;</p>\r\n<div style=\"color:black; background-color:white\">\r\n<pre>    <span style=\"color:gray\">///</span> <span style=\"color:gray\">&lt;summary&gt;</span>\r\n        <span style=\"color:gray\">///</span><span style=\"color:green\"> Returns a byte array of the underlying file data</span>\r\n        <span style=\"color:gray\">///</span> <span style=\"color:gray\">&lt;/summary&gt;</span>\r\n        <span style=\"color:gray\">///</span> <span style=\"color:gray\">&lt;returns&gt;</span><span style=\"color:green\">An array of bytes&lt;/returns&gt;</span>\r\n        <span style=\"color:blue\">public</span> <span style=\"color:blue\">byte</span>[] GetBinary()\r\n        {\r\n            <span style=\"color:green\">//open the web based on the managed file's web property</span>\r\n            <span style=\"color:blue\">using</span> (SPWeb web = activeSite.OpenWeb(<span style=\"color:blue\">this</span>.Web))\r\n            {\r\n                <span style=\"color:green\">//get the item by the managed file's item property (guid).</span>\r\n                SPListItem item = web.Lists[<span style=\"color:blue\">this</span>.List].Items[<span style=\"color:blue\">this</span>.Item];\r\n\r\n                <span style=\"color:green\">//open the file creating a binary stream</span>\r\n                <span style=\"color:blue\">return</span> item.File.OpenBinary();               \r\n            }\r\n        }\r\n</pre>\r\n</div>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2011-02-06T18:52:25.24-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "562233",
    "ThreadId": "244895",
    "Html": "\r\n<p>in the WriteDelegate, you need to actually write the data to the output stream. &nbsp; It is not sufficient to simply assign a value to the output stream variable, which is what your code does.</p>\r\n<p>Like this:</p>\r\n<div style=\"color:black; background-color:white\">\r\n<pre>        <span style=\"color:blue\">private</span> <span style=\"color:blue\">void</span> WriteEntry(String filename, Stream output)\r\n        {\r\n            ManagedFile value = (ManagedFile)hs[filename];\r\n            <span style=\"color:blue\">byte</span>[] b = value.GetBinary();\r\n            output.Write(b,0,b.Length);\r\n        }\r\n\r\n</pre>\r\n</div>\r\n<p>&nbsp;</p>\r\n",
    "PostedDate": "2011-02-07T14:17:36.743-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "562615",
    "ThreadId": "244895",
    "Html": "\r\n<p>Yeap, that was the trick! Works like a charm now. Also by setting that buffer to FALSE, I get a nice effect of the Download starting right away and then just streaming along as it goes!</p>\r\n<p>That probably seems silly, but we're using this function on our Organizational Meetings page and most of our users are non tech savy, so they need something to pop up right away or they think it's broken and they complain and I loose my job and my kids (2\r\n boys, 6 and 4) go hungry! Well okay, maybe not that bad.</p>\r\n<p>One last question, if I wanted to pass the docID variable to the WriteEntry method, so that I didn't have to have a member variable like &quot;hs&quot; defined, would I have to modify the DotNetZip source? or is there a way to provide an override without touching\r\n the DotNotZip DLL?</p>\r\n<p>See how in the whole code I have to have a HashTable called &quot;hs&quot; defined at the class level so that I can get the ManagedFile I am interested in? I wish I could just pass the DocID variable to WriteEntry, but since it's a delegate the signature is set in\r\n stone it seems.</p>\r\n<p>&nbsp;</p>\r\n<p></p>\r\n<pre>using System;\r\nusing System.Collections;\r\nusing System.IO;\r\nusing System.Web;\r\nusing System.Web.UI;\r\nusing Ionic.Zip;\r\nusing Microsoft.SharePoint;\r\n\r\nnamespace MISO.IR.ECM.SP.ApplicationPages\r\n{\r\n    /// \r\n    /// A page that zips a set of files based on query string parameters\r\n    /// \r\n    public class ZipFiles : Page\r\n    {\r\n        Hashtable hs;\r\n      //  private object lockObj = new object();\r\n\r\n        /// \r\n        /// Overrides the base OnLoad method for the zip file response.\r\n        /// \r\n        /// &lt;param name=&quot;e&quot; /&gt;Event Args\r\n        protected override void OnLoad(EventArgs e)\r\n        {\r\n            base.OnLoad(e);\r\n\r\n            //get the query string of document id's\r\n            object queryString = Request.QueryString[&quot;docIDs&quot;];\r\n\r\n            //validate value exists\r\n            if (queryString == null)\r\n                return;\r\n\r\n            //convert the dash '-' seperated string values to an integer array.\r\n            int[] docIDs = GetDocIDs(queryString.ToString());\r\n\r\n            //send the zip file back in the response as an attachment\r\n            SendZipResponse(docIDs);            \r\n        }\r\n\r\n        /// \r\n        /// Outputs the zip file stream to the response output stream\r\n        /// \r\n        /// &lt;param name=&quot;docIDs&quot; /&gt;An array of document id's to include in the zip file.\r\n        private void SendZipResponse(int[] docIDs)\r\n        {\r\n            SPSite site = SPContext.Current.Site;\r\n            Response.Clear();\r\n            //set response parameters for the zip attachment\r\n            Response.Buffer = false;\r\n            Response.Expires = 0;\r\n            Response.ContentType = &quot;application/x-zip-compressed&quot;;\r\n            String guid = Guid.NewGuid().ToString();\r\n            string disHeader = string.Format(&quot;Attachment;Filename=\\&quot;download-{0}.zip\\&quot;&quot;, guid);\r\n            Response.AppendHeader(&quot;Content-Disposition&quot;, disHeader);\r\n\r\n            //create a lock as the zip output stream is not thread safe\r\n            //lock (lockObj)\r\n            //{\r\n            using (ZipFile zip = new ZipFile())\r\n            {\r\n                hs = new Hashtable(docIDs.Length);\r\n                foreach (int docID in docIDs)\r\n                {\r\n                    ManagedFile mf = new ManagedFile(site);\r\n                    mf.ID = docID;\r\n                    mf.Load();\r\n                    hs.Add(Path.GetFileName(mf.URL), mf);\r\n                    zip.AddEntry(Path.GetFileName(mf.URL), WriteEntry);\r\n                }\r\n                zip.Save(Response.OutputStream);\r\n            }\r\n            HttpContext.Current.ApplicationInstance.CompleteRequest();\r\n        }\r\n\r\n        private void WriteEntry(String filename,  Stream output)\r\n        {\r\n            ManagedFile value = (ManagedFile)hs[filename];\r\n            byte[] b = value.GetBinary();\r\n            output.Write(b, 0, b.Length);\r\n        }\r\n\r\n        /// \r\n        /// Converts a dash '-' seperated string of numbers to an integer array\r\n        /// \r\n        /// &lt;param name=&quot;queryString&quot; /&gt;The string to parse.\r\n        /// An array on integers\r\n        private int[] GetDocIDs(string queryString)\r\n        {\r\n            //split the string into an array of number string values\r\n            string[] docIDs = queryString.ToString().Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);\r\n            \r\n            //create a matching sized integer array\r\n            int[] retArray = new int[docIDs.Length];\r\n\r\n            int intVal = 0;\r\n\r\n            //loop through the string values parsing them into the integer array\r\n            for (int i = 0; i &lt; docIDs.Length; i&#43;&#43;)\r\n            {\r\n                if (!int.TryParse(docIDs[i], out intVal))\r\n                    throw new ArgumentException(&quot;Document ID's must be integer values.&quot;);\r\n\r\n                retArray[i] = intVal;\r\n            }\r\n\r\n            return retArray;\r\n        }\r\n    }\r\n}</pre>\r\n<p></p>\r\n",
    "PostedDate": "2011-02-08T05:57:59.063-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "565635",
    "ThreadId": "244895",
    "Html": "<p>You can avoid making the Hashtable an instance variable by converting your WriteEntry to be an Action defined within SendZipResponse. For this you need at least .NET 3.5, I think.&nbsp; An Action is basically a delegate, but it is defined within the SendZipResponse() method, and can be used only there (unless you pass it out via a separate call).&nbsp; Because it is defined within the SendZipResponse() method, the WriteEntry action can access local variables that are in-scope.&nbsp; Move your hashtable variable into that method, and the WriteEntry Action can access it, via closures.&nbsp;</p>\n<p>Also, I'd recommend using the generic Dictionary, instead of the Hashtable type. It gives you a typesafe hashtable.&nbsp; For that you will need to add a \"using System.Collections.Generic;\" At the top of your source file.&nbsp; And, I'd recommend Response.Close() in lieu of mumblefroo.CompleteRequest().</p>\n<p>All those changes look like this in code:&nbsp;</p>\n<p><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: blue;\">private</span> <span style=\"color: blue;\">void</span> SendZipResponse(<span style=\"color: blue;\">int</span>[] docIDs)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; SPSite site = SPContext.Current.Site;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: blue;\">var</span> hs = <span style=\"color: blue;\">new</span> Dictionary&lt;<span style=\"color: blue;\">string</span>,ManagedFile&gt;();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: blue;\">var</span> WriteEntry = <span style=\"color: blue;\">new</span> Action&lt;String,Stream&gt;((filename,output) =&gt; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: blue;\">byte</span>[] b = hs[filename].GetBinary();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; output.Write(b, 0, b.Length);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Clear();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: green;\">//set response parameters for the zip attachment</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Buffer = <span style=\"color: blue;\">false</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.Expires = 0;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.ContentType = <span style=\"color: #a31515;\">\"application/x-zip-compressed\"</span>;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String guid = Guid.NewGuid().ToString();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: blue;\">string</span> disHeader = <span style=\"color: blue;\">string</span>.Format(<span style=\"color: #a31515;\">\"Attachment;Filename=\\\"download-{0}.zip\\\"\"</span>, guid);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Response.AppendHeader(<span style=\"color: #a31515;\">\"Content-Disposition\"</span>, disHeader);<br /><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: green;\">//create a lock as the zip output stream is not thread safe</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: green;\">//lock (lockObj)</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: green;\">//{</span><br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: blue;\">using</span> (ZipFile zip = <span style=\"color: blue;\">new</span> ZipFile())<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <span style=\"color: blue;\">foreach</span> (<span style=\"color: blue;\">int</span> docID <span style=\"color: blue;\">in</span> docIDs)<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagedFile mf = <span style=\"color: blue;\">new</span> ManagedFile(site);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mf.ID = docID;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mf.Load();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hs.Add(Path.GetFileName(mf.URL), mf);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zip.AddEntry(Path.GetFileName(mf.URL), WriteEntry);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; zip.Save(Response.OutputStream);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpContext.Close();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br /><br /><br /></p>",
    "PostedDate": "2011-02-13T19:24:06.093-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "566475",
    "ThreadId": "244895",
    "Html": "<p>Thanks for working so hard on this Cheeso!</p>\r\n<p>Unfortunately this is on SharePoint 2007 install, so it's .Net 2.0 only! :(</p>\r\n<p>I wasn't able to get the sample code working. Action&lt;t&gt; in 2.0 can only take the 1 parameter. I couldn't get the method generated as an Action delegate that made DotNetZip happy with WriteEntry having 2 parameters.</p>\r\n<p>We did alot of testing and it seems that the Dictionary&lt;string, managedfile&gt; is proving very thread safe and VERY fast. We're planning to go with that.</p>\r\n<p>&nbsp;</p>\r\n<p>Once it's done and in Production, I will share the link with you, Cheeso, to see your hard work in action!</p>",
    "PostedDate": "2011-02-15T06:38:53.753-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  },
  {
    "Id": "566541",
    "ThreadId": "244895",
    "Html": "<p>ok, well if you are using .NET 2.0, then you can use the WriteDelegate type. like this:</p>\r\n<div style=\"color: black; background-color: white;\">\r\n<pre>            Ionic.Zip.WriteDelegate WriteEntry = \r\n                <span style=\"color: blue;\">new</span> Ionic.Zip.WriteDelegate((filename,output) =&gt; {\r\n                    <span style=\"color: blue;\">byte</span>[] b = hs[filename].Whatever...\r\n                    output.Write(b, 0, b.Length);\r\n                });\r\n            \r\n\r\n</pre>\r\n</div>\r\n<p>&nbsp;I'm glad the Dictionary is working for you.</p>\r\n<p>Anyway, good luck.</p>\r\n<p>&nbsp;</p>",
    "PostedDate": "2011-02-15T08:31:53.353-08:00",
    "UserRole": null,
    "MarkedAsAnswerDate": null
  }
]