Создание zip-файла от потока и загрузка его

У меня есть 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-файле пуст.

45
задан Navid Farhadi 27 February 2012 в 10:01
поделиться

6 ответов

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 (). Выполняется только одна запись (+ сжатие).

69
ответ дан 26 November 2019 в 21:18
поделиться

Почему вы не закрыли 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 за указание на очевидный недостаток - я пропустил его за милю и не понял их комментария, пока не понял, что поток был в конце ...

6
ответ дан 26 November 2019 в 21:18
поделиться

Добавьте заголовок ContentType:

Response.ContentType = "application/zip";

это позволит браузерам определять, что вы отправляете.

0
ответ дан 26 November 2019 в 21:18
поделиться

Вы пытались очистить поток перед архивированием?

dt.WriteXml(stream);
stream.Flush();
ZipFile zipFile = new ZipFile();
1
ответ дан 26 November 2019 в 21:18
поделиться

Хорошо. Не похоже, что мы продвинулись здесь очень далеко, поэтому вам нужно еще немного заняться отладкой.

Обновите свой код, чтобы сделать следующее:

dt.WriteXml(stream);
stream.Seek(0, SeekOrigin.Begin);
File.WriteAllBytes("c:\test.xml", stream.GetBuffer());

Проверьте, есть ли у вас допустимый XML-файл. Если вы это сделаете, продолжайте делать то же самое со своим ZipFile. Сохраните его в локальный файл. Посмотрите, есть ли там ваш xml-файл и есть ли в вашем xml-файле содержимое.

Если это сработает, попробуйте отправить обратно только поток памяти в качестве ответа, посмотрите, работает ли это.

После этого вы сможете отследить проблему дальше.

1
ответ дан 26 November 2019 в 21:18
поделиться

Дважды проверьте поток, который вы возвращаете обратно. В вашем примере ниже

zipFile.Save(Response.OutputStream);
Response.Write(zipstream);
zipFile.Dispose();

Вы сохраняете zipFile в поток ответа с помощью метода Save, но затем вы также вызываете Response.Write() с переменной zipstream. Что такое zipstream? Проверьте, что это не пустой поток.

0
ответ дан 26 November 2019 в 21:18
поделиться
Другие вопросы по тегам:

Похожие вопросы: