Создание 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;