У меня есть действие в одном из моих контроллеров, который создает загружаемый 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
и записывать файл на диск.
Использование MemoryStream в качестве потока вместо того, чтобы получить массив, который полностью копирует весь контент сразу. Чтение потока также включает в себя копирование, но в меньших фрагментах.
Установите положение потока памяти в начало перед чтением из него:
memoryStream.Position = 0;
Если вы действительно заботитесь о памяти, то вот решение, которое будет напрямую писать в поток ответов. Сначала определите свой пользовательский 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
можно настроить. Здесь я предполагаю, что файлы хранятся в файловой системе.