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 示例 http://dotnetzip.codeplex.com/wikipage?title=Examples,代替调用 Dispose()。 using() 子句在出现故障时更加可靠。
现在你可能想知道,为什么在调用AddEntry()之前需要在MemoryStream中查找呢?原因是,AddEntry() 旨在支持那些传递位置很重要的流的调用者。在这种情况下,调用者需要从流中读取条目数据,使用流的当前位置。 AddEntry() 支持这一点。因此,请在调用 AddEntry() 之前设置流中的位置。
但是,更好的选择是修改您的代码以使用接受 WriteDelegate 的 AddEntry() 的重载 http://cheeso.members.winisp.net/DotNetZipHelp/html/633c4280-51ca-cc82-b5b5-86bbe9b2e947.htm。它是专门为将数据集添加到 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);
}
这会将数据集直接写入 zip 文件中的压缩流中。非常高效!没有双缓冲。匿名委托在 ZipFile.Save() 时被调用。仅执行一次写入(+压缩)。