动态创建一堆URL的动态zip

我正在尝试创建任意大小的zip文件。 zip存档的来源是一堆URL,可能很大(列表中有500个4MB JPG)。 我希望能够在请求中执行所有操作并立即开始下载,并在构建时创建zip并进行流式处理。 它不应该驻留在服务器的内存或磁盘上。

我最接近的是:注意:urls是文件名的URL的keyvaluepair,因为它们应该存在于创建的zip中

Response.ClearContent(); Response.ClearHeaders(); Response.ContentType = "application/zip"; Response.AddHeader("Content-Disposition", "attachment; filename=DyanmicZipFile.zip"); using (var memoryStream = new MemoryStream()) { using (var archive = new ZipArchive(memoryStream, ZipArchiveMode.Create, true)) { foreach (KeyValuePair fileNamePair in urls) { var zipEntry = archive.CreateEntry(fileNamePair.Key); using (var entryStream = zipEntry.Open()) using (WebClient wc = new WebClient()) wc.OpenRead(GetUrlForEntryName(fileNamePair.Key)).CopyTo(entryStream); //this doesn't work either //using (var streamWriter = new StreamWriter(entryStream)) // using (WebClient wc = new WebClient()) // streamWriter.Write(wc.OpenRead(GetUrlForEntryName(fileNamePair.Key))); } } memoryStream.WriteTo(Response.OutputStream); } HttpContext.Current.ApplicationInstance.CompleteRequest(); 

这段代码给了我一个zip文件,但是zip里面的每个JPG文件只是一个文本文件,上面写着“System.Net.ConnectStream”我还有其他的尝试,用它来构建一个包含正确文件的zip文件,但是你可以通过开始时的延迟告诉服务器完全在内存中构建zip,然后在最后将其压缩。 当文件计数接近50时,它根本没有响应。评论中的部分给了我同样的结果我也尝试了Ionic.Zip。

这是IIS8上的.NET 4.5。 我正在使用VS2013构建并尝试在AWS Elastic Beanstalk上运行它。

您正在尝试创建一个zip文件,并在创建时将其流式传输。 事实certificate这非常困难。

您需要了解Zip文件格式 。 特别注意,本地文件条目具有无法更新的头字段(CRC,压缩和未压缩文件大小),直到压缩整个文件。 因此,在将其发送到响应流之前,至少需要缓冲至少一个整个文件。

所以最好你可以这样做:

 open archive for each file create entry write file to entry read entry raw data and send to the response output stream 

您将遇到的问题是,没有记录的方式(并且没有我所知的未记录方式)来读取原始数据。 唯一的读取方法最终解压缩数据并丢弃标头。

可能还有其他一些zip库可以满足您的需求。 我不建议尝试使用ZipArchive

所以回答我自己的问题 – 这是适合我的解决方案:

 private void ProcessWithSharpZipLib() { byte[] buffer = new byte[4096]; ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipOutputStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(Response.OutputStream); zipOutputStream.SetLevel(0); //0-9, 9 being the highest level of compression zipOutputStream.UseZip64 = ICSharpCode.SharpZipLib.Zip.UseZip64.Off; foreach (KeyValuePair fileNamePair in urls) { using (WebClient wc = new WebClient()) { using (Stream wcStream = wc.OpenRead(GetUrlForEntryName(fileNamePair.Key))) { ICSharpCode.SharpZipLib.Zip.ZipEntry entry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(ICSharpCode.SharpZipLib.Zip.ZipEntry.CleanName(fileNamePair.Key)); zipOutputStream.PutNextEntry(entry); int count = wcStream.Read(buffer, 0, buffer.Length); while (count > 0) { zipOutputStream.Write(buffer, 0, count); count = wcStream.Read(buffer, 0, buffer.Length); if (!Response.IsClientConnected) { break; } Response.Flush(); } } } } zipOutputStream.Close(); Response.Flush(); Response.End(); } 

您正在使用的zip组件中必须有一种方法允许将条目延迟添加到存档中,即。 在调用zip.Save()之后添加它们。 我使用延迟技术使用IonicZip,下载flickr专辑的代码如下所示:

 protected void Page_Load(object sender, EventArgs e) { if (!IsLoggedIn()) Response.Redirect("/login.aspx"); else { // this is dco album id, find out what photosetId it maps to string albumId = Request.Params["id"]; Album album = findAlbum(new Guid(albumId)); Flickr flickr = FlickrInstance(); PhotosetPhotoCollection photos = flickr.PhotosetsGetPhotos(album.PhotosetId, PhotoSearchExtras.OriginalUrl | PhotoSearchExtras.Large2048Url | PhotoSearchExtras.Large1600Url); Response.Clear(); Response.BufferOutput = false; // ascii only //string archiveName = album.Title + ".zip"; string archiveName = "photos.zip"; Response.ContentType = "application/zip"; Response.AddHeader("content-disposition", "attachment; filename=" + archiveName); int picCount = 0; string picNamePref = album.PhotosetId.Substring(album.PhotosetId.Length - 6); using (ZipFile zip = new ZipFile()) { zip.CompressionMethod = CompressionMethod.None; zip.CompressionLevel = Ionic.Zlib.CompressionLevel.None; zip.ParallelDeflateThreshold = -1; _map = new Dictionary(); foreach (Photo p in photos) { string pictureUrl = p.Large2048Url; if (string.IsNullOrEmpty(pictureUrl)) pictureUrl = p.Large1600Url; if (string.IsNullOrEmpty(pictureUrl)) pictureUrl = p.LargeUrl; string pictureName = picNamePref + "_" + (++picCount).ToString("000") + ".jpg"; _map.Add(pictureName, pictureUrl); zip.AddEntry(pictureName, processPicture); } zip.Save(Response.OutputStream); } Response.Close(); } } private volatile Dictionary _map; protected void processPicture(string pictureName, Stream output) { HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(_map[pictureName]); using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { using (Stream input = response.GetResponseStream()) { byte[] buf = new byte[8092]; int len; while ( (len = input.Read(buf, 0, buf.Length)) > 0) output.Write(buf, 0, len); } output.Flush(); } } 

这样,Page_Load中的代码立即变为zip.Save(),下载开始(客户端显示“另存为”框,然后才从flickr中提取图像。

这段代码工作正常,但当我在Windows Azure上托管我的代码作为云服务时,它会破坏我的zip文件抛出消息无效文件

 private void ProcessWithSharpZipLib(){ byte[] buffer = new byte[4096]; ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipOutputStream = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(Response.OutputStream); zipOutputStream.SetLevel(0); //0-9, 9 being the highest level of compression zipOutputStream.UseZip64 = ICSharpCode.SharpZipLib.Zip.UseZip64.Off; foreach (KeyValuePair fileNamePair in urls) { using (WebClient wc = new WebClient()) { using (Stream wcStream = wc.OpenRead(GetUrlForEntryName(fileNamePair.Key))) { ICSharpCode.SharpZipLib.Zip.ZipEntry entry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(ICSharpCode.SharpZipLib.Zip.ZipEntry.CleanName(fileNamePair.Key)); zipOutputStream.PutNextEntry(entry); int count = wcStream.Read(buffer, 0, buffer.Length); while (count > 0) { zipOutputStream.Write(buffer, 0, count); count = wcStream.Read(buffer, 0, buffer.Length); if (!Response.IsClientConnected) { break; } Response.Flush(); } } } } zipOutputStream.Close(); Response.Flush(); Response.End(); } 

此代码在本地计算机上正常工作,但在服务器上部署后却没有。 如果它的大小,它会破坏我的zip文件。