Подтвердить что ты не робот

Создание 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 файле пуст.

4b9b3361

Ответ 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());