Создание 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 файле пуст.
Ответы
Ответ 1
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);
}
Даже если вы сохраните этот проект, я бы предложил предложение use(), как я показал, и как описано во всех примерах DotNetZip вместо вызова Dispose(). Предложение using() является более надежным в случае сбоев.
Теперь вы можете задаться вопросом, зачем нужно искать в MemoryStream перед вызовом AddEntry()? Причина в том, что AddEntry() предназначен для поддержки тех вызывающих абонентов, которые передают поток, где важна позиция. В этом случае вызывающему абоненту нужны данные для чтения, которые должны быть прочитаны из потока, используя текущую позицию потока. AddEntry() поддерживает это. Поэтому установите позицию в потоке перед вызовом AddEntry().
Но лучшим вариантом является изменение вашего кода, чтобы использовать перегрузку AddAntry(), которая принимает 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);
}
Это записывает набор данных непосредственно в сжатый поток в zip файле. Очень эффективный! Нет двойной буферизации. Анонимный делегат вызывается во время ZipFile.Save(). Выполняется только одна запись (+ компрессия).
Ответ 2
Почему вы не закрыли 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...
Изменить: Посмотрев на это снова и осознав, что Ionic.Ziplib из Codeplex был использован, я немного изменил код, вместо zipFile.Save(Response.OutputStream);
я использовал zipFile.Save(stream);
с помощью экземпляра stream
класса MemoryStream
и выписал его с помощью Response.Write(stream);
.
Редактировать # 2: Благодаря Cheeso + Mikael для указания на очевидный недостаток - я пропустил его в миле и не понял их комментарий, пока я не понял, что поток был в конце...
Надеюсь, это поможет,
С наилучшими пожеланиями,
Том.
Ответ 3
Вы пытались очистить поток до того, как его застегнули?
dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
Ответ 4
Ok. Похоже, что мы очень далеко здесь, поэтому вам нужно начать отлаживать это немного больше.
Обновите свой код, чтобы сделать следующее:
dt.WriteXml(stream);
stream.Seek(0, SeekOrigin.Begin);
File.WriteAllBytes("c:\test.xml", stream.GetBuffer());
Посмотрите, есть ли у вас действительный файл XML. Если вы это сделаете, сделайте то же самое с ZipFile. Сохраните его в локальном файле. Посмотрите, есть ли там файл xml, и ваш xml файл имеет в нем контент.
Если это работает, попробуйте отправить обратно только поток памяти в качестве ответа, посмотрите, работает ли это.
Затем вы сможете отслеживать проблему дальше.
Ответ 5
Добавить заголовок ContentType:
Response.ContentType = "application/zip";
это позволит браузерам обнаруживать, что вы отправляете.
Ответ 6
Двойная проверка потока, который вы возвращаете обратно. В приведенном ниже примере
zipFile.Save(Response.OutputStream);
Response.Write(zipstream);
zipFile.Dispose();
Вы сохраняете zipFile в поток ответов с помощью метода Save, но затем вы также вызываете Response.Write() с переменной zipstream. Что такое zipstream? Убедитесь, что это тоже пустой поток.
Ответ 7
Этот код поможет вам загрузить файл из потока.
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;
Ответ 8
Создание zip файла из потока и его загрузка. Ниже приведен код.
FileStream stream=File.OpenRead(@"D:\FileDownLoad\DeskTop\1.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());