2010-02-15 135 views
43

我有一個DataTable,我想將它轉換爲xml,然後使用DotNetZip將其壓縮。最終用戶可以通過Asp.Net網頁下載。 我在下面從流中創建Zip文件並下載它

dt.TableName = "Declaration"; 

    MemoryStream stream = new MemoryStream(); 
    dt.WriteXml(stream); 

    ZipFile zipFile = new ZipFile(); 
    zipFile.AddEntry("Report.xml", "", stream); 
    Response.ClearContent(); 
    Response.ClearHeaders(); 
    Response.AppendHeader("content-disposition", "attachment; filename=Report.zip"); 

    zipFile.Save(Response.OutputStream); 
    //Response.Write(zipstream); 
    zipFile.Dispose(); 

XML文件的代碼zip文件是空的。

+3

所以,這有點不工作呢? :) – Rob

+0

zip文件中的xml文件爲空。 –

回答

64

2件事。首先,如果您保留了代碼設計,那麼在將其寫入條目之前,需要在MemoryStream上執行Seek()。

dt.TableName = "Declaration"; 

MemoryStream stream = new MemoryStream(); 
dt.WriteXml(stream); 
stream.Seek(0,SeekOrigin.Begin); // <-- must do this after writing the stream! 

using (ZipFile zipFile = new ZipFile()) 
{ 
    zipFile.AddEntry("Report.xml", "", stream); 
    Response.ClearContent(); 
    Response.ClearHeaders(); 
    Response.AppendHeader("content-disposition", "attachment; filename=Report.zip"); 

    zipFile.Save(Response.OutputStream); 
} 

即使你保留這個設計,我會建議使用一個()子句,因爲我已經證明,並且在所有的DotNetZip examples描述,代替調用Dispose()的。在故障面前,using()子句更可靠。

現在你可能會問,爲什麼在調用AddEntry()之前需要在MemoryStream中尋找?原因是,AddEntry()旨在支持那些通過位置非常重要的流的調用者。在這種情況下,調用者需要使用流的當前位置從流中讀取條目數據。 AddEntry()支持。因此,在調用AddEntry()之前設置流中的位置。

但是,更好的選擇是修改您的代碼以使用overload of AddEntry() that accepts a WriteDelegate。它專爲將數據集添加到zip文件而設計。您的原始代碼將數據集寫入內存流,然後在流上尋找,並將流內容寫入壓縮文件。如果您只寫一次數據,WriteDelegate允許您執行的操作更快更簡單。代碼如下所示:

dt.TableName = "Declaration"; 
Response.ClearContent(); 
Response.ClearHeaders(); 
Response.ContentType = "application/zip"; 
Response.AppendHeader("content-disposition", "attachment; filename=Report.zip"); 

using(Ionic.Zip.ZipFile zipFile = new Ionic.Zip.ZipFile()) 
{ 
    zipFile.AddEntry("Report.xml", (name,stream) => dt.WriteXml(stream)); 
    zipFile.Save(Response.OutputStream); 
} 

這會將數據集直接寫入壓縮文件中的壓縮流中。非常高效!沒有雙緩衝。匿名委託在ZipFile.Save()時被調用。只執行一次寫入(+壓縮)。

+0

短而乾淨:D –

+0

@Cheeso:zipFile.AddEntry沒有這種超載! –

+2

@MeysamJavadi - 它在DotNetZip庫的v1.9中。我承諾。檢查.chm文件。 – Cheeso

0

添加內容類型標頭:

Response.ContentType = "application/zip"; 

這將允許瀏覽器檢測到您所發送的內容。

+0

我嘗試過,它不起作用。 –

4

爲什麼你不關閉MemoryStream,我會將它包裝在using子句中,對於zipFile也可以這麼說呢?此外dt我相信是一個DataTable ...置於錯誤檢查,看看是否有行,見下面的代碼...

 
    dt.TableName = "Declaration"; 

    if (dt.Rows != null && dt.Rows.Count >= 1){ 
     using (MemoryStream stream = new MemoryStream()){ 
     dt.WriteXml(stream); 

     // Thanks Cheeso/Mikael 
     stream.Seek(0, SeekOrigin.Begin); 
     // 

     using (ZipFile zipFile = new ZipFile()){ 
      zipFile.AddEntry("Report.xml", "", stream); 
      Response.ClearContent(); 
      Response.ClearHeaders(); 
      Response.AppendHeader("content-disposition", "attachment; filename=Report.zip"); 

      //zipFile.Save(Response.OutputStream); 
      zipFile.Save(stream); 

      // Commented this out 
      /* 
       Response.Write(zipstream); // <----- Where did that come from? 
      */ 
      } 
      Response.Write(stream); 
     } 
    } 
    // No rows...don't bother... 

編輯:在此之前已經觀察了一遍,意識到Ionic.Ziplib從Codeplex上使用時,我稍微更改了代碼,而不是zipFile.Save(Response.OutputStream);我使用zipFile.Save(stream);使用MemoryStream類的stream實例,並使用Response.Write(stream);將其寫出。

編輯#2:由於Cheeso + 的Mikael您指出了明顯的缺陷 - 我錯過了一英里外,並沒有理解他們的評論,直到我意識到,流在結束...

希望這會有所幫助, 最好的問候, 湯姆。

+0

感謝您的流利的代碼,但那還不行。 –

+0

有用的提示,但關鍵的缺點是在調用AddEntry()之前,原始代碼需要在MemoryStream的開始處搜索()。 – Cheeso

+0

@Cheeso:你爲什麼這麼說?正在仔細檢查你是從哪裏來的... – t0mm13b

1

您是否嘗試在壓縮之前刷新流?

dt.WriteXml(stream); 
stream.Flush(); 
ZipFile zipFile = new ZipFile(); 
0

仔細檢查您正在返回的流。在下面

zipFile.Save(Response.OutputStream); 
Response.Write(zipstream); 
zipFile.Dispose(); 

你比如你是保存壓縮文件使用Save方法的響應流,但你還呼籲的Response.Write()與zipstream變量。什麼是zipstream?檢查它是不是一個空的流。

+0

對不起行 '的Response.Write(zipstream);'必須評論。 –

1

好的。它似乎並沒有像我們在這裏得到很多,所以你需要開始更多的調試。

更新你的代碼執行以下操作:如果你有一個有效的XML文件進行

dt.WriteXml(stream); 
stream.Seek(0, SeekOrigin.Begin); 
File.WriteAllBytes("c:\test.xml", stream.GetBuffer()); 

見。如果你這樣做,那麼繼續前進你的ZipFile。將其保存到本地文件。看看它是否存在,有你的XML文件,你的XML文件中有內容。

如果可行,請嘗試僅回傳內存流作爲響應,看看是否有效。

您應該能夠進一步追蹤問題。

+0

你是對的;所有需要的是在寫入和讀取之間的MemoryStream上的Seek()。 – Cheeso

0

此代碼將幫助您從流中下載文件。

 using (var outStream = new MemoryStream()) 
       { 
        using (var archive = new ZipArchive(outStream, ZipArchiveMode.Create, true)) 
        { 
         var fileInArchive = archive.CreateEntry("FileName.pdf", CompressionLevel.Optimal); 
         using (var entryStream = fileInArchive.Open()) 
         using (WebResponse response = req.GetResponse()) 
         { 
          using (var fileToCompressStream = response.GetResponseStream()) 
          { 
           fileToCompressStream.CopyTo(entryStream); 
          } 
         }      
        } 
        using (var fileStream = new FileStream(@"D:\test.zip", FileMode.Create)) 
        { 
         outStream.Seek(0, SeekOrigin.Begin); 
         outStream.CopyTo(fileStream); 
        } 
       } 

命名空間需要:

using System.IO.Compression; 
using System.IO.Compression.ZipArchive; 
0

創建從流zip文件並下載。以下是代碼。

FileStream stream=File.OpenRead(@"D:\FileDownLoad\DeskTop\1.txt"); 
MemoryStream MS=new MemoryStream(); 

ZipOutputStream zipOutputStream = new ZipOutputStream(MS); 
zipOutputStream.SetLevel(9); 
ZipEntry entry = new ZipEntry("1.txt"); 
zipOutputStream.PutNextEntry(entry); 

byte[] buffer = new byte[stream.Length]; 
int byteRead = 0; 

while ((byteRead = stream.Read(buffer, 0, buffer.Length)) > 0) 
    zipOutputStream.Write(buffer, 0, byteRead); 

    zipOutputStream.IsStreamOwner = false; 
    stream.Close(); 
    zipOutputStream.Close(); 
    MS.Position = 0; 

    Response.ContentType = "application/application/octet-stream"; 
    Response.AppendHeader("content-disposition", "attachment; filename=\"Download.zip\""); 
    Response.BinaryWrite(MS.ToArray());