如果您的解決方案有效,那麼最簡單的方法就是保持原樣。
另一方面,我對您使用DoTNetZip庫有一些評論。
首先,你的代碼有點被誤導了。在本部分中:
byte[] byteInfo = workStream.ToArray();
zip.Save(workStream);
workStream.Write(byteInfo, 0, byteInfo.Length);
workStream.Position = 0;
...您正在將workStream讀入數組中。但是在那個時候,你沒有寫任何東西到workStream,所以數組是空的,零長度。然後將zip保存到工作流中。然後,您將數組(長度爲零)寫入同一個工作流中。這是一個NO-OP。最後你重置位置。
你可以將其替換了這一切:
zip.Save(workStream);
workStream.Position = 0;
這不是DotNetZip本身的問題,這是對你的關於流的操作部分只是一個錯誤的認識。
好吧,接下來,您正在不必要地分配臨時緩衝區(內存流)。將MemoryStream看作只是一個字節數組,其上包含一個Stream封裝器,以支持Write(),Read(),Seek()等。本質上,您的代碼正在將數據寫入臨時緩衝區,然後告訴DotNetZip將臨時緩衝區中的數據讀入其自己的緩衝區中進行壓縮。你不需要臨時緩衝區。它按照你所做的方式工作,但效率可能更高。
DotNetZip有一個AddEntry()
重載接受一個作家代理。代理是DotNetZip調用的一個函數,用於告訴您的應用程序將條目內容寫入zip存檔。您的代碼寫入未壓縮的字節,並且DotNetZip壓縮並將它們寫入輸出流。
在該作家的代表,你的代碼直接寫入DotNetZip流 - 傳遞到由DotNetZip委託流。沒有中間緩衝區。效率不錯。
請記住有關關閉的規則。如果您在for循環中調用此作者委託,則需要獲取與委託中的zipentry相對應的「bla」。在調用zip.Save()
之前,代表不會被執行!所以你不能依賴循環中'bla'的值。
public FileStreamResult DownloadPDF()
{
MemoryStream workStream = new MemoryStream();
using(var zip = new ZipFile())
{
foreach(Bla bla in Blas)
{
zip.AddEntry(bla.filename + ".pdf", (name,stream) => {
var thisBla = GetBlaFromName(name);
Document document = new Document();
PdfWriter.GetInstance(document, stream).CloseStream = false;
document.Open();
// write PDF Content for thisBla into stream/PdfWriter
document.Close();
});
}
zip.Save(workStream);
}
workStream.Position = 0;
FileStreamResult fileResult = new FileStreamResult(workStream, System.Net.Mime.MediaTypeNames.Application.Zip);
fileResult.FileDownloadName = "MultiplePDFs.zip";
return fileResult;
}
最後,我特別不喜歡你從MemoryStream
一個FileStreamResult
的創建。問題是你的整個zip文件保存在內存中,這對內存使用來說可能非常困難。如果您的zip文件很大,您的代碼將保留內存中的所有內容。
我不充分了解MVC3模型知道有東西在裏面,與此幫助。如果沒有,您可以use an Anonymous Pipe to invert the direction of the streams,並且不需要將所有壓縮數據保存在內存中。
這裏就是我的意思是:創建一個FileStreamResult
要求您提供一個可讀的流。如果您使用MemoryStream,爲了使其可讀,您需要先寫入它,然後回到位置0,然後將它傳遞給FileStreamResult
構造函數。這意味着該zip文件的所有內容必須在某個時間點連續存儲在內存中。
假設你可以提供一個可讀的流至FileStreamResult
構造,這將讓讀者在正是你寫信給它的那一刻讀取。這是一個匿名管道流。它允許您的代碼使用可寫入的流,而MVC代碼獲取其可讀流。
下面是它在代碼中的樣子。
static Stream GetPipedStream(Action<Stream> writeAction)
{
AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream();
ThreadPool.QueueUserWorkItem(s =>
{
using (pipeServer)
{
writeAction(pipeServer);
pipeServer.WaitForPipeDrain();
}
});
return new AnonymousPipeClientStream(pipeServer.GetClientHandleAsString());
}
public FileStreamResult DownloadPDF()
{
var readable =
GetPipedStream(output => {
using(var zip = new ZipFile())
{
foreach(Bla bla in Blas)
{
zip.AddEntry(bla.filename + ".pdf", (name,stream) => {
var thisBla = GetBlaFromName(name);
Document document = new Document();
PdfWriter.GetInstance(document, stream).CloseStream = false;
document.Open();
// write PDF Content for thisBla to PdfWriter
document.Close();
});
}
zip.Save(output);
}
});
var fileResult = new FileStreamResult(readable, System.Net.Mime.MediaTypeNames.Application.Zip);
fileResult.FileDownloadName = "MultiplePDFs.zip";
return fileResult;
}
我沒有試過這個,但它應該工作。這與你寫的內容相比具有更高的內存效率。缺點是它比使用命名管道和幾個匿名函數要複雜得多。
這使得僅當拉鍊含量是進入> 1MB範圍感。如果你的拉鍊比這更小,那麼你可以按照我上面展示的第一種方式來做。
附錄
爲什麼你不依賴於匿名方法中的bla
價值?
有兩個關鍵點。首先,foreach循環定義了一個名爲bla
的變量 ,該變量取值不同,每次都通過 循環。看起來很明顯,但值得明確說明它 。
其次,匿名方法被傳遞作爲參數到 的ZipFile.AddEntry()
方法,它不會在當時運行 foreach循環運行。事實上,匿名方法被重複調用 ,一次爲每個條目添加,在 ZipFile.Save()
時。如果你指的bla
匿名 方法中,它得到最後一個值分配給bla
,因爲 是價值bla
當時ZipFile.Save()
運行成立。
這是導致困難的延期執行。
你想要的是在調用匿名函數時可以訪問foreach循環中的每個不同的值bla
,以後在foreach循環之外訪問。您可以使用實用程序方法(GetBlaForName()
)來完成此操作,如上所示。您可以 也有附加的封閉做到這一點,就像這樣:
Action<String,Stream> GetEntryWriter(Bla bla)
{
return new Action<String,Stream>((name,stream) => {
Document document = new Document();
PdfWriter.GetInstance(document, stream).CloseStream = false;
document.Open();
// write PDF Content for bla to PdfWriter
document.Close();
};
}
foreach(var bla in Blas)
{
zip.AddEntry(bla.filename + ".pdf", GetEntryWriter(bla));
}
的GetEntryWriter
回報的方法 - 實際上是一個動作,這僅僅是一個類型化的方法。每次循環時,都會創建該Action的新實例,併爲bla引用一個不同的值。該行動直到ZipFile.Save()
時才被調用。
+1謝謝你給出了一個很好的細分和高效的代碼!你能否擴展一下爲什麼我不能依賴循環中'bla'的值。 –
是的,我在上面的答案末尾加上了解釋。如果你想了解更多,你應該閱讀關閉。 http://stackoverflow.com/a/428624/48082 – Cheeso
謝謝!我真的很感謝你的答案的詳細程度! –