У меня есть DataTable, что я хочу преобразовать его в xml и затем архивировать его, с помощью DotNetZip. наконец, пользователь может загрузить его через Asp. Сетевая веб-страница. Мой код в ниже
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-файле пуст.
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);
}
Это записывает набор данных непосредственно в сжатый поток в zip-файле. Очень эффективный! Нет двойной буферизации. Анонимный делегат вызывается во время ZipFile.Save (). Выполняется только одна запись (+ сжатие).
Почему вы не закрыли MemoryStream, я бы заключил это в , используя предложение
, то же самое можно сказать и о 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);
, используя экземпляр потока
класса MemoryStream
, и запишите его, используя Response.Write (поток);
.
Правка №2: Спасибо Cheeso + Mikael за указание на очевидный недостаток - я пропустил его за милю и не понял их комментария, пока не понял, что поток был в конце ...
Добавьте заголовок ContentType:
Response.ContentType = "application/zip";
это позволит браузерам определять, что вы отправляете.
Вы пытались очистить поток перед архивированием?
dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
Хорошо. Не похоже, что мы продвинулись здесь очень далеко, поэтому вам нужно еще немного заняться отладкой.
Обновите свой код, чтобы сделать следующее:
dt.WriteXml(stream);
stream.Seek(0, SeekOrigin.Begin);
File.WriteAllBytes("c:\test.xml", stream.GetBuffer());
Проверьте, есть ли у вас допустимый XML-файл. Если вы это сделаете, продолжайте делать то же самое со своим ZipFile. Сохраните его в локальный файл. Посмотрите, есть ли там ваш xml-файл и есть ли в вашем xml-файле содержимое.
Если это сработает, попробуйте отправить обратно только поток памяти в качестве ответа, посмотрите, работает ли это.
После этого вы сможете отследить проблему дальше.
Дважды проверьте поток, который вы возвращаете обратно. В вашем примере ниже
zipFile.Save(Response.OutputStream);
Response.Write(zipstream);
zipFile.Dispose();
Вы сохраняете zipFile в поток ответа с помощью метода Save, но затем вы также вызываете Response.Write() с переменной zipstream. Что такое zipstream? Проверьте, что это не пустой поток.