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.
Avez-vous réinitialisé la position du flux? –
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
@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