2009-11-06 58 views
2

我在我的一個控制器中創建了一個可下載的zip文件,該文件應該提供給用戶。目前我的代碼看起來是這樣的:ASP.NET MVC:從byte []返回File結果是個好主意嗎?

using(var memoryStream = new MemoryStream()) { 
    // ... use SharpZipLib to write zip file content to the above MemoryStream ... 
    return File(memoryStream.ToArray(), "application/zip", "file.zip"); 
} 

我想知道,如果它是一個好主意去轉換的MemoryStream到byte[],我想這需要更多的存儲則使用流? File()有一個過載,它需要一個Stream對象,並且我傳入了我的memoryStream變量,但只是出現了一個空白頁面。

理想情況下,我不必使用FileStream並將文件寫入磁盤。

+0

您是否重置了流的位置? – 2009-11-06 14:10:14

+0

也許你應該在製作壓縮文件之前估計一下大小,然後在磁盤上使用文件,而不是在文件大的時候保存內存。對於大文件而言,網絡帶寬將成爲瓶頸,而不是磁盤操作。 – Guffa 2009-11-06 14:21:56

+0

@Guffa:爲了仍然使用MemoryStream,會是一個「理智」的大小?該應用程序在4 GB的單個服務器上運行,我不期望高負載,並沒有太多的並行下載。使用內存流對我來說更容易一些,因爲我沒有對舊zip文件進行清理...... – Max 2009-11-06 14:38:28

回答

3

使用MemoryStream作爲流而不是獲取數組不會立即複製所有內容。讀取流還涉及到複製,但是這是小塊。

套裝內存流的開始從中讀取之前的位置:

memoryStream.Position = 0; 
+1

由於整個zip文件將被SharpZipLib存儲在內存流中,這仍然會佔用大量內存。 – 2009-11-06 14:15:17

+0

@Darin:當然可以。這就是爲什麼最好將它看作一個流來消除所有數據的另一個副本。 – Guffa 2009-11-06 14:17:42

5

如果你真的在乎內存中,然後在這裏是一個將直接寫入響應流的解決方案。首先定義自定義的ActionResult:

public class SharpZipLibResult : FileResult 
{ 
    private readonly string _fileDownloadName; 
    private readonly string[] _filesToZip; 
    private const int ChunkSize = 1024; 

    public SharpZipLibResult(string fileDownloadName, params string[] filesToZip) 
     : base("application/octet-stream") 
    { 
     _fileDownloadName = fileDownloadName; 
     _filesToZip = filesToZip; 
    } 

    protected override void WriteFile(HttpResponseBase response) 
    { 
     var cd = new ContentDisposition(); 
     cd.FileName = _fileDownloadName; 
     response.AddHeader("Content-Disposition", cd.ToString()); 
     response.BufferOutput = false; 
     using (var zipStream = new ZipOutputStream(response.OutputStream)) 
     { 
      foreach (var file in _filesToZip) 
      { 
       var entry = new ZipEntry(Path.GetFileName(file)); 
       zipStream.PutNextEntry(entry); 
       using (var reader = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
       { 
        byte[] buffer = new byte[ChunkSize]; 
        int bytesRead; 
        while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0) 
        { 
         byte[] actual = new byte[bytesRead]; 
         Buffer.BlockCopy(buffer, 0, actual, 0, bytesRead); 
         zipStream.Write(actual, 0, actual.Length); 
        } 
       } 
      } 
     } 
    } 
} 

有了這個技術,你可以成爲一些非常巨大的zip文件,而無需關心存儲或有清潔您的服務器硬盤的一些臨時zip文件。

最後你的控制器動作看起來是這樣的:

public ActionResult Index() 
{ 
    return new SharpZipLibResult(
     "result.zip", 
     @"c:\work\report1.pdf", 
     @"c:\work\report2.pdf", 
     @"c:\work\report3.pdf" 
    ); 
} 

使用這種方法的內存佔用量最小化,因爲拉鍊被直接寫入這方面將通過基礎網絡套接字來表示響應流。

當然取決於你的文件存儲在哪裏SharpZipLibResult可以調整。這裏我假設這些文件存儲在文件系統中。

+0

也考慮響應緩衝。如果將所有內容都緩存在內存中,則無需花費大量工作直接寫入響應流... – Guffa 2009-11-06 17:29:39

+0

當然,但我認爲OP正在尋找一種解決方案,通過避免緩存整個zip文件來最大限度地減少內存消耗在記憶中。在我的例子中,整個zip文件從不加載到內存中。它正在動態創建,並以1KB的塊形式直接寫入響應流。 – 2009-11-07 00:26:03

相關問題