2015-04-21 38 views
9

我有一個web api GET方法,它返回一個用於下載的zip文件。下面是一個創建zip壓縮代碼:創建和下載zip壓縮文件時的內存使用情況爲HttpContent

var resultStream = new MemoryStream();  
using (var zipArchive = new ZipArchive(resultStream, ZipArchiveMode.Create, leaveOpen: true)) 
{ 
    foreach (var file in files) 
    { 
     zipArchive.CreateEntryFromFile(file.Path, file.Name, CompressionLevel.Optimal); 
    } 
} 

而這裏的反應如何被填充:

var response = new HttpResponseMessage(HttpStatusCode.OK); 
response.Content = new ByteArrayContent(resultStream.ToArray()); 
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip"); 
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); 
response.Content.Headers.ContentDisposition.FileName = "export_" + DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss") + ".zip"; 
response.Content.Headers.ContentDisposition.CreationDate = DateTime.Now; 
response.Content.Headers.ContentDisposition.Size = resultStream.Length; 
response.Content.Headers.ContentLength = resultStream.Length; 

上面的代碼工作得很好,但問題是它消耗大量的內存在服務器上,當然取決於文件大小。我嘗試將結果更改爲StreamContent,但是這不起作用,因爲響應只返回標題並最終超時。

因此,這裏是我的問題:

  1. 有沒有一種方法,以避免加載在內存中的所有文件,而是因爲它被創建發送壓縮文件?
  2. 更好地使用StreamContent在這種情況下使用,如果是的話,我需要改變什麼才能使它工作?
  3. 在每種情況下緩衝如何影響內存消耗?我已經嘗試通過執行自定義IHostBufferPolicySelector來禁用緩衝,建議in this article,但它似乎沒有任何效果。
  4. 目前可以通過導航鏈接,使用HttpClient或AJAX請求來調用api操作,因此任何解決方案都必須支持所有場景。
+3

嘗試使用GZipStream和PushStreamContent –

+0

您在服務器上獲得了多少請求? –

+0

儘管如此,您將無法設置內容長度,因此需要啓用塊編碼。 –

回答

3

Kudu項目,在組合使用PushStreamContent與特定DelegatingStream包裝到流zip存檔的方法改編:

public static class ZipStreamContent 
{ 
    public static PushStreamContent Create(string fileName, Action<ZipArchive> onZip) 
    { 
     var content = new PushStreamContent((outputStream, httpContent, transportContext) => 
     { 
      using (var zip = new ZipArchive(new StreamWrapper(outputStream), ZipArchiveMode.Create, leaveOpen: false)) 
      { 
       onZip(zip); 
      } 
     }); 
     content.Headers.ContentType = new MediaTypeHeaderValue("application/zip"); 
     content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); 
     content.Headers.ContentDisposition.FileName = fileName; 
     return content;   
    } 

    // this wraps the read-only HttpResponseStream to support ZipArchive Position getter. 
    public class StreamWrapper : DelegatingStream 
    { 
     private long _position = 0; 

     public StreamWrapper(Stream stream) 
      : base(stream) 
     { 
     } 

     public override long Position 
     { 
      get { return _position; } 
      set { throw new NotSupportedException(); } 
     } 

     public override void Write(byte[] buffer, int offset, int count) 
     { 
      _position += count; 
      base.Write(buffer, offset, count); 
     } 

     public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) 
     { 
      _position += count; 
      return base.BeginWrite(buffer, offset, count, callback, state); 
     } 
    } 
} 

這對於你的情況,你可以使用這樣的:

var response = new HttpResponseMessage(HttpStatusCode.OK); 
var response.Content = ZipStreamContent.Create(
    "export_" + DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss") + ".zip", 
    zipArchive => { 
     foreach (var file in files) 
     { 
      zipArchive.CreateEntryFromFile(file.Path, file.Name, CompressionLevel.Optimal); 
     }   
    }); 
+0

謝謝Alex,什麼是DelegatingStream,我該如何使用它? – elolos

+2

Err,您可能需要對此進行一點處理:ASP.NET堆棧(命名空間System.Net.Http)中的內部抽象類,您可以在其中找到它的源代碼(http:// referencesource。 microsoft.com/#System.Net.Http/System/Net/Http/DelegatingStream.cs) – Alex

+0

剛剛嘗試過,似乎'PushStreamContent'是一個很好的解決方案,內存使用率現在非常低。 但是,當zip方法中出現異常時,我遇到了一個問題,響應失敗,但響應狀態仍然爲200,這不幸中斷了任何AJAX調用。 – elolos

相關問題