2009-03-09 6 views
12

Je crée un service de téléchargement simple afin qu'un utilisateur puisse télécharger toutes ses images depuis le site. Pour ce faire, je viens de tout zip au flux http.Diffusion d'un fichier zip sur http dans .net avec SharpZipLib

Cependant, il semble que tout est stocké en mémoire, et les données ne sont pas envoyées jusqu'à ce que le fichier zip soit complet et la sortie fermée. Je souhaite que le service commence à envoyer immédiatement et n'utilise pas trop de mémoire.

public void ProcessRequest(HttpContext context) 
{ 
    List<string> fileNames = GetFileNames(); 
    context.Response.ContentType = "application/x-zip-compressed"; 
    context.Response.AppendHeader("content-disposition", "attachment; filename=files.zip"); 
    context.Response.ContentEncoding = Encoding.Default; 
    context.Response.Charset = ""; 

    byte[] buffer = new byte[1024 * 8]; 

    using (ICSharpCode.SharpZipLib.Zip.ZipOutputStream zipOutput = new ICSharpCode.SharpZipLib.Zip.ZipOutputStream(context.Response.OutputStream)) 
    { 
     foreach (string fileName in fileNames) 
     { 
      ICSharpCode.SharpZipLib.Zip.ZipEntry zipEntry = new ICSharpCode.SharpZipLib.Zip.ZipEntry(fileName); 
      zipOutput.PutNextEntry(zipEntry); 
      using (var fread = System.IO.File.OpenRead(fileName)) 
      { 
       ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fread, zipOutput, buffer); 
      } 
     } 
     zipOutput.Finish(); 
    } 

    context.Response.Flush(); 
    context.Response.End(); 
} 

Je peux voir la mémoire du processus de travail de plus en plus alors qu'il rend le fichier, puis libère la mémoire lorsque son envoi fait. Comment est-ce que je fais cela sans utiliser trop de mémoire?

Répondre

11

Disable response buffering avec context.Response.BufferOutput = false; et de supprimer l'appel Flush à la fin de votre code.

+0

+1. Réponse.Et est dur et inutile aussi. – AnthonyWJones

+0

Il y a d'autres façons de gérer des fichiers très volumineux, je crois, permettant à des requêtes séparées d'obtenir des segments séparés de la réponse. Je ferais zipper le fichier sur le disque pour pouvoir le servir dans plusieurs requêtes sans avoir à le relancer à chaque fois. –

0

utilisez Response.BufferOutput = false; au début de ProcessRequest et vider la réponse après chaque fichier.

+1

Le Flush ne fait rien d'utile lorsque la mise en tampon est désactivée. – AnthonyWJones

0

Pour votre information. C'est le code de travail pour ajouter récursivement une arborescence entière de fichiers, avec le streaming au navigateur:

string path = @"c:\files"; 

Response.Clear(); 
Response.ContentType = "application/zip"; 
Response.AddHeader("Content-Disposition", string.Format("attachment; filename=\"{0}\"", "hive.zip")); 
Response.BufferOutput = false; 

byte[] buffer = new byte[1024 * 1024]; 
using (ZipOutputStream zo = new ZipOutputStream(Response.OutputStream, 1024 * 1024)) { 
    zo.SetLevel(0); 
    DirectoryInfo di = new DirectoryInfo(path); 
    foreach (string file in Directory.GetFiles(di.FullName, "*.*", SearchOption.AllDirectories)) { 
     string folder = Path.GetDirectoryName(file); 
     if (folder.Length > di.FullName.Length) { 
      folder = folder.Substring(di.FullName.Length).Trim('\\') + @"\"; 
     } else { 
      folder = string.Empty; 
     } 
     zo.PutNextEntry(new ZipEntry(folder + Path.GetFileName(file))); 
     using (FileStream fs = File.OpenRead(file)) { 
      ICSharpCode.SharpZipLib.Core.StreamUtils.Copy(fs, zo, buffer); 
     } 
     zo.Flush(); 
     Response.Flush(); 
    } 
    zo.Finish(); 
} 

Response.Flush();