从流创建Zip文件并下载它

时间:2010-02-15 13:29:08

标签: 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();

zip文件中的xml文件为空。

8 个答案:

答案 0 :(得分: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); 
}

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

  

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

但是,更好的选择是修改代码以使用overload of AddEntry() that accepts a WriteDelegate。它专为将数据集添加到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); 
}

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

答案 1 :(得分: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...

修改:再次查看此内容,并意识到使用了Codeplex中的Ionic.Ziplib,我稍微更改了代码,而不是zipFile.Save(Response.OutputStream);我使用zipFile.Save(stream);使用stream类的MemoryStream实例并使用Response.Write(stream);将其写出来。

编辑#2:感谢 Cheeso + Mikael 指出明显的缺陷 - 我错过了一英里的距离,直到我意识到这条小溪已经结束时才理解他们的评论......

答案 2 :(得分:1)

您是否尝试在压缩前刷新流?

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

答案 3 :(得分:1)

确定。看起来好像我们在这里走得很远所以你需要开始调试这个。

更新您的代码以执行以下操作:

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

查看您是否有有效的XML文件。如果你这样做,那么继续使用你的ZipFile。将其保存到本地文件。看看它是否存在,您的xml文件和xml文件中是否包含内容。

如果可行,请尝试仅回送内存流作为响应,看看是否有效。

然后,您应该能够进一步跟踪问题。

答案 4 :(得分:0)

添加ContentType标题:

Response.ContentType = "application/zip";

这将允许浏览器检测您要发送的内容。

答案 5 :(得分:0)

仔细检查您要返回的流。在下面的示例中

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

您正在使用Save方法将zipFile保存到响应流,但是您还使用zipstream变量调用Response.Write()。什么是zipstream?检查它也不是空流。

答案 6 :(得分: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;

答案 7 :(得分:0)

从流创建zip文件并下载它。下面是代码。

id