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