Создание Zip-файла из потока и его загрузка
У меня есть DataTable, который я хочу преобразовать в xml, а затем zip, используя 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();
xml-файл в zip-файле пуст.
8 ответов
2 вещи. Во-первых, если вы сохраняете дизайн кода, который у вас есть, вам нужно выполнить Seek() на MemoryStream перед записью его в запись.
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 () является более надежным перед лицом сбоев.
теперь вы можете задаться вопросом, почему это необходимо искать в MemoryStream перед вызовом AddEntry ()? Причина в том, что AddEntry () предназначен для поддержки тех абонентов, которые передают поток, где важна позиция. В этом случае вызывающему абоненту нужны данные записи для чтения из потока,используя текущее положение потока. AddEntry () поддерживает это. Поэтому задайте позицию в потоке перед вызовом AddEntry ().
но лучший вариант-изменить свой код, чтобы использовать перегрузка AddEntry (), который принимает WriteDelegate. Он был разработан специально для добавления данных в 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.Спасать.)( Выполняется только одна запись (+сжатие).
почему вы не закрыли MemoryStream, я бы завернул это в using
предложение, то же самое можно сказать для zipFile
? Также dt
Я предполагаю, что это 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...
Edit: еще раз взглянув на это и поняв, что ионные.Ziplib из Codeplex был использован, я немного изменил код, а не zipFile.Save(Response.OutputStream);
Я zipFile.Save(stream);
С помощью stream
экземпляр MemoryStream
класс и записать его с помощью Response.Write(stream);
.
Edit#2: спасибо Cheeso + Микаэла
вы пытались смыть поток перед промелькиванием?
dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
Ok. Похоже, мы не очень далеко продвинулись, поэтому вам нужно начать отладку этого немного больше.
Update вы код, чтобы сделать следующее:
dt.WriteXml(stream);
stream.Seek(0, SeekOrigin.Begin);
File.WriteAllBytes("c:\test.xml", stream.GetBuffer());
посмотрите, есть ли у вас допустимый XML-файл. Если вы это сделаете, то продолжайте делать то же самое с вашим ZipFile. Сохраните его в локальном файле. Посмотрите, есть ли там ваш xml-файл, и ваш xml-файл имеет в нем содержимое.
Если это работает, попробуйте отправить только поток памяти в качестве ответа, посмотрите, если это завод.
затем вы сможете отслеживать проблему дальше.
добавить заголовок ContentType:
Response.ContentType = "application/zip";
Это позволит браузерам определить, что вы отправляете.
дважды проверьте поток, который вы возвращаете обратно. В вашем примере ниже
zipFile.Save(Response.OutputStream);
Response.Write(zipstream);
zipFile.Dispose();
вы сохраняете zipFile в поток ответов с помощью метода Save, но затем вы также вызываете Response.Write () с переменной zipstream. Что zipstream? Убедитесь,что это не пустой поток.
создание 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());
этот код поможет вам в загрузке файлов из потока.
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;