Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
506 views
in Technique[技术] by (71.8m points)

c# - Creating Zip file from stream and downloading it

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

    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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

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.

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

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:

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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...