2012-10-16 15 views
5

Sto provando a creare un file zip in un metodo MVC utilizzando i componenti DotNetZip.Utilizzo di memorystream e DotNetZip in MVC fornisce "Impossibile accedere a un flusso chiuso"

Ecco il mio codice:

public FileResult DownloadImagefilesAsZip() 
    { 
     using (var memoryStream = new MemoryStream()) 
     { 
      using (var zip = new ZipFile()) 
      { 
       zip.AddDirectory(Server.MapPath("/Images/")); 
       zip.Save(memoryStream); 

       return File(memoryStream, "gzip", "images.zip"); 
      } 
     } 
    } 

quando l'eseguo ottengo "Impossibile accedere un flusso chiuso" l'errore, e io non so perché.

risposta

15

Non smaltire il MemoryStream, il FileStreamResult si prenderà cura una volta che ha finito di scrivere alla risposta:

public ActionResult DownloadImagefilesAsZip() 
{ 
    var memoryStream = new MemoryStream(); 
    using (var zip = new ZipFile()) 
    { 
     zip.AddDirectory(Server.MapPath("~/Images")); 
     zip.Save(memoryStream); 
     return File(memoryStream, "application/gzip", "images.zip"); 
    } 
} 

proposito vi consiglio di scrivere di conseguenza un'azione personalizzata per gestire questa situazione, invece di scrivere il codice dell'impianto idraulico all'interno dell'azione del controller. Non solo avrai un risultato d'azione riutilizzabile, ma ricordati che il tuo codice è estremamente inefficiente => stai eseguendo l'operazione ZIP all'interno della memoria e quindi carichi l'intero contenuto della directory ~/images + il file zip in memoria. Se ci sono molti utenti e molti file in questa directory, la memoria si esaurirà molto rapidamente.

Un molto più efficiente soluzione è quella di scrivere direttamente al flusso di risposta:

public class ZipResult : ActionResult 
{ 
    public string Path { get; private set; } 
    public string Filename { get; private set; } 

    public ZipResult(string path, string filename) 
    { 
     Path = path; 
     Filename = filename; 
    } 

    public override void ExecuteResult(ControllerContext context) 
    { 
     if (context == null) 
     { 
      throw new ArgumentNullException("context"); 
     } 

     var response = context.HttpContext.Response; 
     response.ContentType = "application/gzip"; 
     using (var zip = new ZipFile()) 
     { 
      zip.AddDirectory(Path); 
      zip.Save(response.OutputStream); 
      var cd = new ContentDisposition 
      { 
       FileName = Filename, 
       Inline = false 
      }; 
      response.Headers.Add("Content-Disposition", cd.ToString()); 
     } 
    } 
} 

e poi:

public ActionResult DownloadImagefilesAsZip() 
{ 
    return new ZipResult(Server.MapPath("~/Images"), "images.zip"); 
} 
+0

Perfetto! Grazie mille :) –

+1

Ho dovuto aggiungere 'memoryStream.Seek (0, SeekOrigin.Begin);' prima 'return File (...);' per farlo funzionare. – pqvst

0

non poter commentare.

La risposta di Darin è eccezionale! Ancora ricevuto un'eccezione di memoria anche se così ha dovuto aggiungere response.BufferOutput = false; e per questo motivo è stato necessario spostare il codice di disposizione dei contenuti più in alto.

in modo da avere:

... 
     var response = context.HttpContext.Response; 
     response.ContentType = "application/zip"; 
     response.BufferOutput = false; 

     var cd = new ContentDisposition 
     { 
      FileName = ZipFilename, 
      Inline = false 
     }; 
     response.Headers.Add("Content-Disposition", cd.ToString()); 

     using (var zip = new ZipFile()) 
     { 
... 

Solo nel caso in cui non era ovvio :)