Потоковая передача zip-файла по http в .net с помощью SharpZipLib

Я делаю простой сервис загрузки, чтобы пользователь мог загружать все свои изображения с другого сайта. Для этого я просто заархивирую все в поток http.

Однако кажется, что все хранится в памяти, и данные не отправляются до завершения zip-файла и закрытия вывода. Я хочу, чтобы служба сразу начала отправлять и не использовала слишком много памяти.

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

Я вижу, как память рабочего процесса увеличивается, пока он создает файл, а затем освобождает память после завершения отправки. Как мне это сделать, не используя слишком много памяти?


person AndreasN    schedule 09.03.2009    source источник


Ответы (3)


Отключите буферизацию ответов с помощью context.Response.BufferOutput = false; и удалите Flush вызов с конца вашего кода.

person Jon Skeet    schedule 09.03.2009
comment
Я полагаю, что есть и другие способы обработки очень больших файлов, позволяющие отдельным запросам получать отдельные фрагменты ответа. Я бы заархивировал файл на диск, чтобы иметь возможность обслуживать его в нескольких запросах без необходимости каждый раз архивировать. - person Jon Skeet; 16.04.2009

используйте Response.BufferOutput = false; при запуске ProcessRequest и сбросить ответ после каждого файла.

person Alex Reitbort    schedule 09.03.2009
comment
Flush не делает ничего полезного, когда буферизация отключена. - person AnthonyWJones; 09.03.2009

к вашему сведению. Это рабочий код для рекурсивного добавления всего дерева файлов с потоковой передачей в браузер:

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();
person Fredrik Johansson    schedule 09.08.2010