从流创建Zip文件并下载我

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

Navid Farhadi asked 2020-07-11T14:49:55Z
8个解决方案
69 votes

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()时将调用匿名委托。 仅执行一次写入(+压缩)。

Cheeso answered 2020-07-11T14:50:23Z
6 votes

您为什么不关闭MemoryStream,我将其包裹在2968872181359281152子句中,所以对于zipFile.Save(stream);可以这样说? 我也认为stream是一个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(stream);(使用stream类的stream类的实例),并使用Response.Write(stream);进行写出。

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

t0mm13b answered 2020-07-11T14:50:53Z
1 votes

您是否曾尝试在压缩之前冲洗流?

dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
jmservera answered 2020-07-11T14:51:13Z
1 votes

好。 看起来我们似乎还没有走的太远,所以您需要开始进行更多调试。

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

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

查看是否有有效的XML文件。 如果您这样做,请继续执行ZipFile。 将其保存到本地文件。 看看它是否在那里,具有您的xml文件,并且您的xml文件中包含内容。

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

然后,您应该能够进一步找到问题所在。

Ian answered 2020-07-11T14:51:51Z
0 votes

添加内容类型标题:

Response.ContentType = "application/zip";

这将使浏览器能够检测到您发送的内容。

Mikael Svenson answered 2020-07-11T14:52:15Z
0 votes

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

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

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

Ian answered 2020-07-11T14:52:40Z
0 votes

从流中创建一个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());
Sheo Dayal Singh answered 2020-07-11T14:52:59Z
0 votes

此代码将帮助您从流中下载文件。

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;
Skull answered 2020-07-11T14:53:24Z
translate from https://stackoverflow.com:/questions/2266204/creating-zip-file-from-stream-and-downloading-it