从流创建Zip文件并下载它

Nav*_*adi 45 c# asp.net zip stream dotnetzip

我有一个DataTable,我想将它转换为xml,然后使用DotNetZip压缩它.最后用户可以通过Asp.Net网页下载.我的代码在下面

    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();
Run Code Online (Sandbox Code Playgroud)

zip文件中的xml文件为空.

Che*_*eso 69

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); 
}
Run Code Online (Sandbox Code Playgroud)

即使你保留了这个设计,我也建议使用一个using()子句,如我所示,并且如所有DotNetZip示例所述,代替调用Dispose().面对失败,using()子句更可靠.

现在您可能想知道,为什么在调用AddEntry()之前需要在MemoryStream中寻找?原因是,AddEntry()旨在支持那些传递位置很重要的流的调用者.在这种情况下,调用者需要使用的当前位置从流中读取条目数据.AddEntry()支持它.因此,在调用AddEntry()之前在流中设置位置.

但是,更好的选择是修改代码以使用接受WriteDelegate的AddEntry()重载.它专为将数据集添加到zip文件而设计.您的原始代码将数据集写入内存流,然后在流上搜索,并将流的内容写入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); 
}
Run Code Online (Sandbox Code Playgroud)

这会将数据集直接写入zipfile中的压缩流中.效率很高!没有双缓冲.在ZipFile.Save()时调用匿名委托.仅执行一次写入(+压缩).

  • @MeysamJavadi - 它在DotNetZip库的v1.9中.我承诺.检查.chm文件. (3认同)

t0m*_*13b 6

你为什么不关闭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);使用stream的实例MemoryStream类,并用它写出来Response.Write(stream);.

编辑#2:感谢Cheeso + Mikael指出了明显的缺陷 - 我错过了一英里的距离并且直到我意识到流在最后才明白他们的评论......