2009-11-06 1 views
2

J'ai une action dans un de mes contrôleurs qui crée un fichier zip téléchargeable qui devrait être servi à l'utilisateur. À l'heure actuelle mon code ressemble à ceci:ASP.NET MVC: est-ce une bonne idée de renvoyer un résultat de fichier à partir d'un octet []?

using(var memoryStream = new MemoryStream()) { 
    // ... use SharpZipLib to write zip file content to the above MemoryStream ... 
    return File(memoryStream.ToArray(), "application/zip", "file.zip"); 
} 

Je me demande si elle est une bonne idée aller convertir MemoryStream à un byte[], je suppose que cela prend plus de mémoire puis en utilisant le flux? Il y a une surcharge à File() qui prend un objet Stream, et j'ai passé dans ma variable memoryStream, mais juste une page blanche est apparue.

Idéalement je ne dois utiliser un FileStream et écrire un fichier sur le disque.

+0

Avez-vous réinitialisé la position du flux? –

+0

Peut-être que vous devriez faire une estimation de la taille avant de faire le zip, et utiliser le fichier sur le disque à la place si cela va être grand, pour préserver la mémoire. Pour un gros fichier, la bande passante du réseau sera le goulot d'étranglement et non l'opération du disque. – Guffa

+0

@Guffa: Quelle serait une taille "saine" afin d'utiliser encore le MemoryStream? L'application fonctionne sur un seul serveur avec 4 Go et je ne m'attends pas à une charge élevée et pas beaucoup de téléchargements parallèles. L'utilisation d'un flux de mémoire est un peu plus facile pour moi que je n'ai pas à faire un nettoyage sur les anciens fichiers zip ... – Max

Répondre

3

Utilisation du MemoryStream comme un flux au lieu d'obtenir les elliminates de tableau copie tout le contenu à la fois. La lecture du flux implique également la copie, mais c'est en plus petits morceaux.

Régler la position du flux de mémoire au début avant la lecture de celui-ci:

memoryStream.Position = 0; 
+1

Cela pourrait encore prendre beaucoup de mémoire que le zip entier sera stocké dans le flux de mémoire par SharpZipLib. –

+0

@Darin: Oui bien sûr.C'est pourquoi il est préférable de le lire comme un flux pour éliminer une autre copie de toutes les données. – Guffa

5

Si vous tenez vraiment à la mémoire puis est ici une solution qui directement écrire dans le flux de réponse. Tout d'abord définir votre ActionResult personnalisée:

public class SharpZipLibResult : FileResult 
{ 
    private readonly string _fileDownloadName; 
    private readonly string[] _filesToZip; 
    private const int ChunkSize = 1024; 

    public SharpZipLibResult(string fileDownloadName, params string[] filesToZip) 
     : base("application/octet-stream") 
    { 
     _fileDownloadName = fileDownloadName; 
     _filesToZip = filesToZip; 
    } 

    protected override void WriteFile(HttpResponseBase response) 
    { 
     var cd = new ContentDisposition(); 
     cd.FileName = _fileDownloadName; 
     response.AddHeader("Content-Disposition", cd.ToString()); 
     response.BufferOutput = false; 
     using (var zipStream = new ZipOutputStream(response.OutputStream)) 
     { 
      foreach (var file in _filesToZip) 
      { 
       var entry = new ZipEntry(Path.GetFileName(file)); 
       zipStream.PutNextEntry(entry); 
       using (var reader = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) 
       { 
        byte[] buffer = new byte[ChunkSize]; 
        int bytesRead; 
        while ((bytesRead = reader.Read(buffer, 0, buffer.Length)) > 0) 
        { 
         byte[] actual = new byte[bytesRead]; 
         Buffer.BlockCopy(buffer, 0, actual, 0, bytesRead); 
         zipStream.Write(actual, 0, actual.Length); 
        } 
       } 
      } 
     } 
    } 
} 

Avec cette technique, vous pouvez servir des fichiers zip vraiment énormes sans se soucier de la mémoire ou d'avoir à nettoyer certains fichiers zip temporaires sur vos disques durs du serveur.

Et enfin votre action de contrôleur pourrait ressembler à ceci:

public ActionResult Index() 
{ 
    return new SharpZipLibResult(
     "result.zip", 
     @"c:\work\report1.pdf", 
     @"c:\work\report2.pdf", 
     @"c:\work\report3.pdf" 
    ); 
} 

utilisant cette méthode empreinte mémoire est réduite au minimum parce que le zip est écrit directement au flux de réponse qui, en termes sera représenté par une prise de réseau sous-jacent.

Bien sûr, en fonction de l'endroit où vos fichiers sont stockés le SharpZipLibResult pourrait être modifié. Ici, je suppose que les fichiers sont stockés sur le système de fichiers.

+0

Envisager la mise en mémoire tampon des réponses également. – Guffa

+0

Bien sûr, mais je pensais que l'OP cherchait une solution pour minimiser la consommation de mémoire en évitant de mettre en mémoire tampon toute la fermeture éclair. en mémoire. Dans mon exemple, le zip entier n'est jamais chargé en mémoire. Il est créé dynamiquement et écrit directement dans le flux de réponse en blocs de 1 Ko. –

Questions connexes