50

我有一个 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 文件为空。

4

8 回答 8

71

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 示例中所述,代替调用 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); 
}

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

于 2010-02-15T17:32:47.277 回答
6

你为什么不关闭 MemoryStream,我会把它包装在一个using子句中,同样可以这样说zipFile?另外dt我认为是一个数据表...进行错误检查以查看是否有行,请参见下面的代码...

    dt.TableName = "声明";

    if (dt.Rows != null && dt.Rows.Count >= 1){
      使用 (MemoryStream 流 = new MemoryStream()){
         dt.WriteXml(流);

         // 谢谢奶酪/米凯尔
         stream.Seek(0, SeekOrigin.Begin);
         //

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

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

             // 注释掉这个
             /*
               Response.Write(zipstream); // <----- 这是从哪里来的?
             */
          }
          Response.Write(流);
       }
    }
    // 没有行...不要打扰...

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

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

于 2010-02-15T14:04:01.617 回答
1

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

dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
于 2010-02-15T14:13:15.440 回答
1

行。看起来我们在这里并没有走得太远,所以你需要开始更多地调试它。

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

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

看看你是否有一个有效的 XML 文件。如果你这样做了,那么继续对你的 ZipFile 做同样的事情。将其保存到本地文件。看看它是否在那里,有你的 xml 文件并且你的 xml 文件中有内容。

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

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

于 2010-02-15T15:12:24.007 回答
0

添加 ContentType 标头:

Response.ContentType = "application/zip";

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

于 2010-02-15T13:46:08.187 回答
0

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

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

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

于 2010-02-15T14:21:13.960 回答
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;
于 2015-12-17T12:15:47.357 回答
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());
于 2016-09-04T17:17:43.067 回答