C# 从流创建 Zip 文件并下载它

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2266204/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-07 00:54:04  来源:igfitidea点击:

Creating Zip file from stream and downloading it

c#asp.netzipstreamdotnetzip

提问by Navid Farhadi

I have a DataTable that i want to convert it to xml and then zip it, using DotNetZip. finally user can download it via Asp.Net webpage. My code in below

我有一个数据表,我想将它转换为 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();

the xml file in zip file is empty.

zip 文件中的 xml 文件为空。

采纳答案by Cheeso

2 things. First, if you keep the code design you have, you need to perform a Seek() on the MemoryStream before writing it into the entry.

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); 
}

Even if you keep this design, I would suggest a using() clause, as I have shown, and as described in all the DotNetZip examples, in lieu of calling Dispose(). The using() clause is more reliable in the face of failures.

即使您保留这种设计,我也会建议使用 using() 子句,正如我所展示的,以及所有DotNetZip 示例中所述,来代替调用 Dispose()。using() 子句在遇到故障时更可靠。

Now you may wonder, why is it necessary to seek in the MemoryStream before calling AddEntry()? The reason is, AddEntry() is designed to support those callers who pass a stream where the position is important. In that case, the caller needs the entry data to be read from the stream, using the current position of the stream. AddEntry() supports that. Therefore, set the position in the stream before calling AddEntry().

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

But, the better option is to modify your code to use the overload of AddEntry() that accepts a WriteDelegate. It was designed specifically for adding datasets into zip files. Your original code writes the dataset into a memory stream, then seeks on the stream, and writes the content of the stream into the zip. It's faster and easier if you write the data once, which is what the WriteDelegate allows you to do. The code looks like this:

但是,更好的选择是修改您的代码以使用接受 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); 
}

This writes the dataset directly into the compressed stream in the zipfile. Very efficient! No double-buffering. The anonymous delegate is called at the time of ZipFile.Save(). Only one write (+compress) is performed.

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

回答by Mikael Svenson

Add a ContentType header:

添加一个 ContentType 标头:

Response.ContentType = "application/zip";

this will allow the browsers to detect what you are sending.

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

回答by t0mm13b

Why did you not close the MemoryStream, I would wrap that in a usingclause, the same could be said for zipFile? Also dtI presume is a DataTable...put in error checking to see if there's rows, see the code below...

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

    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...

Edit:Having looked at this again, and realizing that Ionic.Ziplibfrom Codeplex was used, I changed the code slightly, instead of zipFile.Save(Response.OutputStream);I used zipFile.Save(stream);using the streaminstance of the MemoryStreamclass and write it out using Response.Write(stream);.

编辑:在这之前已经观察了一遍,意识到Ionic.Ziplib从Codeplex上使用,我略微改变的代码,而不是zipFile.Save(Response.OutputStream);我用zipFile.Save(stream);使用stream的实例MemoryStream类,并用它写出来Response.Write(stream);

Edit#2:Thanks to Cheeso+ Mikaelfor pointing out the obvious flaw - I missed it a mile off and did not understood their comment until I realized that the stream was at the end...

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

回答by jmservera

Have you tried to flush the stream before zipping?

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

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

回答by Ian

Double check the stream you are returning back too. In your example below

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

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

You are saving the zipFile to your response stream using the Save method, but then you are also calling Response.Write() with a zipstream variable. What is zipstream? Check that it isn't an empty stream too.

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

回答by Ian

Ok. It doesn't seem like we are getting very far here so you need to start debugging this a bit more.

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

Update you're code to do the following:

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

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

See if you have a valid XML file out. If you do then move on do the same with your ZipFile. Save it to a local file. See if it's there, has your xml file and your xml file has content in it.

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

If that works, try sending back just the memory stream as the response, see if that works.

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

You should then be able to track the problem down further.

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

回答by Skull

This code will help you in downloading a file from stream.

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

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);
    }
}

Namespaces needed:

需要的命名空间:

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

回答by Sheo Dayal Singh

Creating a zip file from stream and downloading it. Below is the code.

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

FileStream stream=File.OpenRead(@"D:\FileDownLoad\DeskTop.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());