2010-02-15 18 views
43

Tengo una DataTable que quiero convertir a xml y luego comprimirla, usando DotNetZip. finalmente el usuario puede descargarlo a través de la página web de Asp.Net. Mi código de abajoCreando un archivo Zip desde la secuencia y descargándolo

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(); 

el archivo XML en el archivo zip está vacía.

+3

Entonces, ¿qué bit no funciona entonces? :) – Rob

+0

el archivo xml en el archivo zip está vacío. –

Respuesta

64

2 cosas. Primero, si conserva el diseño del código que tiene, debe realizar una Búsqueda() en el MemoryStream antes de escribirlo en la entrada.

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); 
} 

Incluso si se mantiene este diseño, sugeriría el uso de una cláusula(), como he mostrado, y como se describe en toda la DotNetZip examples, en lugar de llamar a Dispose(). La cláusula using() es más confiable ante fallas.

Ahora puede preguntarse, ¿por qué es necesario buscar en el MemoryStream antes de llamar a AddEntry()? El motivo es que AddEntry() está diseñado para admitir a los llamantes que pasan una transmisión donde la posición es importante. En ese caso, la persona que llama necesita que los datos de entrada se lean de la secuencia, usando la posición actual de la corriente. AddEntry() lo admite. Por lo tanto, establezca la posición en la secuencia antes de llamar AddEntry().

Pero, la mejor opción es modificar su código para usar el overload of AddEntry() that accepts a WriteDelegate. Fue diseñado específicamente para agregar conjuntos de datos en archivos zip. Su código original escribe el conjunto de datos en una secuencia de memoria, luego busca en la secuencia y escribe el contenido de la secuencia en el archivo zip. Es más rápido y más fácil si escribe los datos una vez, que es lo que WriteDelegate le permite hacer. El código se ve así:

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); 
} 

Esto escribe el conjunto de datos directamente en la secuencia comprimida en el archivo zip. ¡Muy eficiente! Sin doble buffer. El delegado anónimo se llama en el momento de ZipFile.Save(). Solo se realiza una escritura (+ compresión).

+0

Corto y limpio: D –

+0

@ Cheeso: zipFile.AddEntry no tiene esta sobrecarga. –

+2

@MeysamJavadi - lo hace, en v1.9 de la biblioteca DotNetZip. Lo prometo. Verifique el archivo .chm. – Cheeso

0

agregar un encabezado ContentType:

Response.ContentType = "application/zip"; 

Esto permitirá a los navegadores para detectar lo que está enviando.

+0

Lo intento, la dosis no funciona. –

4

¿Por qué no cerraste el MemoryStream, lo envolvería en una cláusula using, lo mismo podría decirse de zipFile? También dt supongo que es un DataTable ... puesto en la comprobación de errores para ver si hay filas, ver el código de abajo ...

 
    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... 

Editar: Tras mirar esto otra vez, y darse cuenta de que Ionic.Ziplib desde CodePlex se usó, cambié el código ligeramente, en lugar de zipFile.Save(Response.OutputStream); utilicé zipFile.Save(stream); usando la instancia stream de la clase MemoryStream y la escribí usando Response.Write(stream);.

Edición # 2: Gracias a Cheeso + Mikael por señalar la falla obvia - no me di cuenta de una milla de distancia y no entendían su comentario hasta que me di cuenta de que la corriente estaba en el final ...

Espero que esto ayude, Saludos cordiales, Tom.

+0

Gracias por su código fluido, pero eso aún no funciona. –

+0

Sugerencias útiles, pero el punto clave que falta es que el código original necesita Buscar() al principio de MemoryStream, antes de llamar AddEntry(). – Cheeso

+0

@ Cheeso: ¿por qué dices eso? Estoy escudriñando para ver de dónde vienes ... – t0mm13b

1

¿Ha intentado vaciar la corriente antes de comprimir?

dt.WriteXml(stream); 
stream.Flush(); 
ZipFile zipFile = new ZipFile(); 
0

doble comprobar el flujo va a devolver la espalda también. En el siguiente ejemplo

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

está guardando el archivo zip a su secuencia de respuesta mediante el método Save, pero entonces también está llamando Response.Write() con una variable zipstream. ¿Qué es zipstream? Verifique que no sea una transmisión vacía también.

+0

Lamento la línea 'Response.Write (zipstream);' debe ser un comentario. –

1

Ok. No parece que lleguemos muy lejos, así que debes comenzar a depurar esto un poco más.

actualización estás código para hacer lo siguiente:

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

Ver si tiene un archivo XML válido. Si lo haces, entonces sigue haciendo lo mismo con tu ZipFile. Guárdelo en un archivo local. Vea si está allí, tiene su archivo xml y su archivo xml tiene contenido.

Si eso funciona, intente enviar solo la secuencia de la memoria como respuesta, vea si eso funciona.

Debería poder rastrear el problema más adelante.

+0

Estás en lo correcto; todo lo que se necesita es un Seek() en el MemoryStream, entre escritura y lectura. – Cheeso

0

Este código lo ayudará a descargar un archivo de la transmisión.

 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); 
        } 
       } 

Espacio de nombres necesarios:

using System.IO.Compression; 
using System.IO.Compression.ZipArchive; 
0

Creación de un archivo zip de flujo y descarga. A continuación está el código.

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()); 
Cuestiones relacionadas