2016-11-29 147 views
0

在我的網站上,當用戶單擊某個按鈕時,一堆文件必須在zip中歸檔併發送出去。這些文件本身是由第三部分生成的,我只有這些URL。 我已經取得了部分成功,但我有一些問題。在創建時發送一個zip文件

首先,如果有大量文件要壓縮,服務器響應速度很慢,因爲它首先生成zip文件,然後發送它。它甚至會在一段時間後崩潰(特別是,我得到錯誤「算術運算溢出或下溢,」)。

其次,現在文件只有在zip存檔完成後纔會發送。我希望下載立即開始。也就是說,只要用戶點擊對話框中的「保存」,數據就會開始發送,並隨着zip文件「即時創建」而不斷髮送。我在一些網站上看到了這個功能,例如:http://download.muuto.com/

問題是,我不知道該怎麼做。

我已經使用的部分代碼來自這個問題:Creating a dynamic zip of a bunch of URLs on the fly 而從這個博客帖子:http://dejanstojanovic.net/aspnet/2015/march/generate-zip-file-on-the-fly-in-aspnet-mvc-application/

本身被創建並在ASP.NET MVC控制器方法返回的zip文件。這裏是我的代碼:

using ICSharpCode.SharpZipLib.Zip; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Web; 
using System.Web.Mvc; 

namespace MyProject.Controllers 
{ 
    public class MyController : Controller 
    {   
     public ActionResult DownloadFiles() 
     { 
      var files = SomeFunction(); 

      byte[] buffer = new byte[4096]; 

      var baseOutputStream = new MemoryStream(); 
      ZipOutputStream zipOutputStream = new ZipOutputStream(baseOutputStream); 
      zipOutputStream.SetLevel(0); //0-9, 9 being the highest level of compression 
      zipOutputStream.UseZip64 = UseZip64.Off; 
      zipOutputStream.IsStreamOwner = false; 

      foreach (var file in files) 
      { 
       using (WebClient wc = new WebClient()) 
       { 
        // We open the download stream of the file 
        using (Stream wcStream = wc.OpenRead(file.Url)) 
        { 
         ZipEntry entry = new ZipEntry(ZipEntry.CleanName(file.FileName)); 
         zipOutputStream.PutNextEntry(entry); 

         // As we read the stream, we add its content to the new zip 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; 
          } 
         } 
        } 
       } 
      } 
      zipOutputStream.Finish(); 
      zipOutputStream.Close(); 

      // Set position to 0 so that cient start reading of the stream from the begining 
      baseOutputStream.Position = 0; 

      // Set custom headers to force browser to download the file instad of trying to open it 
      return new FileStreamResult(baseOutputStream, "application/x-zip-compressed") 
      { 
       FileDownloadName = "Archive.zip" 
      }; 
     } 
    } 
} 
+0

可能的複製。網與SharpZipLib](http://stackoverflow.com/questions/626196/streaming-a-zip-file-over-http-in-net-with-sharpziplib) –

+0

絕對相關(相同的目標),但不重複:我試圖從MVC控制器中實現,這是一種不同的方法。我嘗試以不同的方式使用'BufferOutput = false',但似乎沒有太大的改變。 – Ogier

+0

這並沒有什麼不同。您可以返回一個接受流作爲參數的'FileResult' –

回答

1

通過擺弄了一下與響應的OutputStream和緩衝好吧,我抵達一個解決方案:在以上HTTP流zip文件的

using ICSharpCode.SharpZipLib.Zip; 
using System; 
using System.Collections.Generic; 
using System.IO; 
using System.Linq; 
using System.Net; 
using System.Web; 
using System.Web.Mvc; 

namespace MyProject.Controllers 
{ 
    public class MyController : Controller 
    {   
     public ActionResult DownloadFiles() 
     { 
      var files = SomeFunction(); 

      // Disable Buffer Output to start the download immediately 
      Response.BufferOutput = false; 

      // Set custom headers to force browser to download the file instad of trying to open it 
      Response.ContentType = "application/x-zip-compressed"; 
      Response.AppendHeader("content-disposition", "attachment; filename=Archive.zip"); 

      byte[] buffer = new byte[4096]; 

      ZipOutputStream zipOutputStream = new ZipOutputStream(Response.OutputStream); 
      zipOutputStream.SetLevel(0); // No compression 
      zipOutputStream.UseZip64 = UseZip64.Off; 
      zipOutputStream.IsStreamOwner = false; 

      try 
      { 
       foreach (var file in files) 
       { 
        using (WebClient wc = new WebClient()) 
        { 
         // We open the download stream of the image 
         using (Stream wcStream = wc.OpenRead(file.Url)) 
         { 
          ZipEntry entry = new ZipEntry(ZipEntry.CleanName(file.FileName)); 
          zipOutputStream.PutNextEntry(entry); 

          // As we read the stream, we add its content to the new zip 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; 
           } 
          } 
         } 
        } 
       } 
      } 
      finally 
      { 
       zipOutputStream.Finish(); 
       zipOutputStream.Close(); 
      } 

      return new HttpStatusCodeResult(HttpStatusCode.OK); 
     } 
    } 
}