ASP.NET MVC: хорошая ли идея вернуть результат File из байта []?

2

У меня есть действие в одном из моих контроллеров, который создает загружаемый zip файл, который должен быть отправлен пользователю. В настоящее время мой код выглядит примерно так:

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

Мне интересно, стоит ли переводить memoryStream в byte[], я думаю, это занимает больше памяти, а затем использует поток? Существует перегрузка на File(), которая принимает объект Stream, и я передал в свою переменную memoryStream, но затем появилась только пустая страница.

В идеале мне не нужно использовать FileStream и записывать файл на диск.

  • 0
    Вы сбросили положение потока?
  • 0
    Возможно, вам следует оценить размер перед созданием zip-файла и использовать файл на диске, если он будет большим, чтобы сохранить память. Для большого файла пропускная способность сети будет узким местом, а не работой диска.
Показать ещё 5 комментариев
Теги:
asp.net-mvc

2 ответа

3
Лучший ответ

Использование MemoryStream в качестве потока вместо того, чтобы получить массив, который полностью копирует весь контент сразу. Чтение потока также включает в себя копирование, но в меньших фрагментах.

Установите положение потока памяти в начало перед чтением из него:

memoryStream.Position = 0;
  • 1
    Это все еще может занять много памяти, поскольку весь zip-файл будет сохранен в потоке памяти SharpZipLib.
  • 0
    @ Darin: Да, конечно. Вот почему лучше читать это как поток, чтобы устранить еще одну копию всех данных.
5

Если вы действительно заботитесь о памяти, то вот решение, которое будет напрямую писать в поток ответов. Сначала определите свой пользовательский ActionResult:

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

С помощью этой методики вы можете обслуживать действительно огромные ZIP файлы, не заботясь о памяти или не чистить временные файлы ZIP на жестких дисках вашего сервера.

И, наконец, действие вашего контроллера может выглядеть так:

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

С помощью этого метода размер памяти памяти минимизируется, поскольку zip записывается непосредственно в поток ответов, который в терминах будет представлен базовым сетевым сокетом.

Конечно, в зависимости от того, где хранятся ваши файлы, SharpZipLibResult можно настроить. Здесь я предполагаю, что файлы хранятся в файловой системе.

  • 0
    Рассмотрим также буферизацию ответов. Нет смысла тратить много времени на запись непосредственно в поток ответов, если он все равно буферизируется в памяти ...
  • 0
    Конечно, но я подумал, что ОП ищет решение, чтобы минимизировать потребление памяти, избегая буферизации всего почтового индекса в памяти. В моем примере весь zip никогда не загружается в память. Он создается динамически и записывается непосредственно в поток ответов кусками по 1 КБ.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню