创建和下载zip存档作为HttpContent时的内存使用情况 [英] Memory usage when creating and downloading zip archive as HttpContent

查看:74
本文介绍了创建和下载zip存档作为HttpContent时的内存使用情况的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Web api GET方法,该方法返回一个zip文件进行下载.这是创建zip归档文件的代码:

I have a web api GET method that returns a zip file for downloading. Here's the code that creates the zip archive:

var resultStream = new MemoryStream();    
using (var zipArchive = new ZipArchive(resultStream, ZipArchiveMode.Create, leaveOpen: true))
{
    foreach (var file in files)
    {
        zipArchive.CreateEntryFromFile(file.Path, file.Name, CompressionLevel.Optimal);
    }
}

这是响应的填充方式:

var response = new HttpResponseMessage(HttpStatusCode.OK);
response.Content = new ByteArrayContent(resultStream.ToArray());
response.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
response.Content.Headers.ContentDisposition.FileName = "export_" + DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss") + ".zip";
response.Content.Headers.ContentDisposition.CreationDate = DateTime.Now;
response.Content.Headers.ContentDisposition.Size = resultStream.Length;
response.Content.Headers.ContentLength = resultStream.Length;

上面的代码工作得很好,问题在于它会消耗服务器上的大量内存,具体取决于文件大小.我尝试将结果更改为 StreamContent ,但是由于响应仅返回标头并最终超时,因此此方法不起作用.

The code above works just fine, the problem is it consumes a lot of memory on the server, depending of course on the file sizes. I've tried changing the result to StreamContent, however this didn't work as the response only returned headers and eventually timed out.

这是我的问题:

  1. 是否有办法避免将所有文件加载到内存中,而是在创建zip文件时发送该文件?
  2. 在这种情况下使用StreamContent更好吗?如果可以,我需要更改哪些内容才能使其正常工作?
  3. 缓冲在每种情况下如何影响内存消耗?我尝试通过建议
  1. Is there a way to avoid loading all files in memory and instead send the zip file as it gets created?
  2. Is using StreamContent better to use in this scenario and if yes, what do I need to change to make it work?
  3. How is buffering affecting memory consumption in each case? I've tried disabling buffering by implementing a custom IHostBufferPolicySelector as suggested in this article, but it doesn't appear to have any effect.
  4. The api action currently can be called by navigating a link, using HttpClient or by AJAX request, so any solution has to support all scenarios.

推荐答案

改编自

Adapted from the Kudu project, a method that uses PushStreamContent in combination with a specific DelegatingStream wrapper to stream a zip archive:

public static class ZipStreamContent
{
    public static PushStreamContent Create(string fileName, Action<ZipArchive> onZip)
    {
        var content = new PushStreamContent((outputStream, httpContent, transportContext) =>
        {
            using (var zip = new ZipArchive(new StreamWrapper(outputStream), ZipArchiveMode.Create, leaveOpen: false))
            {
                onZip(zip);
            }
        });
        content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
        content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
        content.Headers.ContentDisposition.FileName = fileName;
        return content;        
    }

    // this wraps the read-only HttpResponseStream to support ZipArchive Position getter.
    public class StreamWrapper : DelegatingStream
    {
        private long _position = 0;

        public StreamWrapper(Stream stream)
            : base(stream)
        {
        }

        public override long Position
        {
            get { return _position; }
            set { throw new NotSupportedException(); }
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            _position += count;
            base.Write(buffer, offset, count);
        }

        public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state)
        {
            _position += count;
            return base.BeginWrite(buffer, offset, count, callback, state);
        }
    }
}

对于您的情况,您可以这样使用:

Which for your case you could use like:

var response = new HttpResponseMessage(HttpStatusCode.OK);
var response.Content = ZipStreamContent.Create(
    "export_" + DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss") + ".zip",
    zipArchive => {
        foreach (var file in files)
        {
            zipArchive.CreateEntryFromFile(file.Path, file.Name, CompressionLevel.Optimal);
        }        
    });

这篇关于创建和下载zip存档作为HttpContent时的内存使用情况的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆