在 ASP.NET 核心中实现下载 > 2gb 的大文件

Implement Downloading Large files > 2gb in ASP.NET Core

这是我的代码:

var net = new System.Net.WebClient();
var data = net.DownloadData(zipPath);
var content = new MemoryStream(data);
var contentType = "APPLICATION/octet-stream";
var fileName = zipPath.Split('\')[zipPath.Split('\').Length -1];
Response.Cookies.Append("download", "finished");
return File(content, contentType, fileName);

但是,DownloadData(zipPath) 给出了 WebException 错误:“超出了消息长度限制” 似乎它不能读取超过 2GB 的大小,我搜索了一下,它需要编辑一些 属性 到 -1 的对象,我没有在我的代码中使用它.. https://social.msdn.microsoft.com/Forums/en-US/88d0c0bb-ec86-435d-9d2a-c5ec821e9a79/httpwebresponse-maximum-response-header-size-response-over-64k-problem?forum=winappswithcsharp

我也试过了:

HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK);
var stream = new FileStream(zipPath, FileMode.Open, FileAccess.Read);
result.Content = new StreamContent(stream);
result.Content.Headers.ContentType =
new MediaTypeHeaderValue("application/octet-stream");

给出了这个例外:System.IO.IOException:'The file is too long. This operation is currently limited to supporting files less than 2 gigabytes in size.'

我不知道该怎么办了..有什么建议吗? 最后,我尝试 return File(byte[]) 以便我可以从服务器下载该文件。

尝试从同一个 I/O 调用 return 项目 > 2GB 永远不会有好结果。在任何时候,您都不需要单个 byte[] 来包含项目的全部内容。相反,在处理如此大的项目时,您希望获得 stream,您一次只能读取和缓冲小段。您可以使用 WebClient.GetWebRequest()WebClient.GetWebResponse() 来完成此操作。

感谢所有尝试帮助我的人,在努力挖掘文档并重新检查答案、结合想法等之后,我自己找到了解决方案。 您有两个解决方案:

1- 制作一个 class 将 Stream 实现为 HugeMemoryStream,当然你可以在这里找到它:

class HugeMemoryStream : System.IO.Stream
{
    #region Fields

    private const int PAGE_SIZE = 1024000000;
    private const int ALLOC_STEP = 1024;

    private byte[][] _streamBuffers;

    private int _pageCount = 0;
    private long _allocatedBytes = 0;

    private long _position = 0;
    private long _length = 0;

    #endregion Fields

    #region Internals

    private int GetPageCount(long length)
    {
        int pageCount = (int)(length / PAGE_SIZE) + 1;

        if ((length % PAGE_SIZE) == 0)
            pageCount--;

        return pageCount;
    }

    private void ExtendPages()
    {
        if (_streamBuffers == null)
        {
            _streamBuffers = new byte[ALLOC_STEP][];
        }
        else
        {
            byte[][] streamBuffers = new byte[_streamBuffers.Length + ALLOC_STEP][];

            Array.Copy(_streamBuffers, streamBuffers, _streamBuffers.Length);

            _streamBuffers = streamBuffers;
        }

        _pageCount = _streamBuffers.Length;
    }

    private void AllocSpaceIfNeeded(long value)
    {
        if (value < 0)
            throw new InvalidOperationException("AllocSpaceIfNeeded < 0");

        if (value == 0)
            return;

        int currentPageCount = GetPageCount(_allocatedBytes);
        int neededPageCount = GetPageCount(value);

        while (currentPageCount < neededPageCount)
        {
            if (currentPageCount == _pageCount)
                ExtendPages();

            _streamBuffers[currentPageCount++] = new byte[PAGE_SIZE];
        }

        _allocatedBytes = (long)currentPageCount * PAGE_SIZE;

        value = Math.Max(value, _length);

        if (_position > (_length = value))
            _position = _length;
    }

    #endregion Internals

    #region Stream

    public override bool CanRead => true;

    public override bool CanSeek => true;

    public override bool CanWrite => true;

    public override long Length => _length;

    public override long Position
    {
        get { return _position; }
        set
        {
            if (value > _length)
                throw new InvalidOperationException("Position > Length");
            else if (value < 0)
                throw new InvalidOperationException("Position < 0");
            else
                _position = value;
        }
    }

    public override void Flush() { }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int currentPage = (int)(_position / PAGE_SIZE);
        int currentOffset = (int)(_position % PAGE_SIZE);
        int currentLength = PAGE_SIZE - currentOffset;

        long startPosition = _position;

        if (startPosition + count > _length)
            count = (int)(_length - startPosition);

        while (count != 0 && _position < _length)
        {
            if (currentLength > count)
                currentLength = count;

            Array.Copy(_streamBuffers[currentPage++], currentOffset, buffer, offset, currentLength);

            offset += currentLength;
            _position += currentLength;
            count -= currentLength;

            currentOffset = 0;
            currentLength = PAGE_SIZE;
        }

        return (int)(_position - startPosition);
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        switch (origin)
        {
            case SeekOrigin.Begin:
                break;

            case SeekOrigin.Current:
                offset += _position;
                break;

            case SeekOrigin.End:
                offset = _length - offset;
                break;

            default:
                throw new ArgumentOutOfRangeException("origin");
        }

        return Position = offset;
    }

    public override void SetLength(long value)
    {
        if (value < 0)
            throw new InvalidOperationException("SetLength < 0");

        if (value == 0)
        {
            _streamBuffers = null;
            _allocatedBytes = _position = _length = 0;
            _pageCount = 0;
            return;
        }

        int currentPageCount = GetPageCount(_allocatedBytes);
        int neededPageCount = GetPageCount(value);

        // Removes unused buffers if decreasing stream length
        while (currentPageCount > neededPageCount)
            _streamBuffers[--currentPageCount] = null;

        AllocSpaceIfNeeded(value);

        if (_position > (_length = value))
            _position = _length;
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        int currentPage = (int)(_position / PAGE_SIZE);
        int currentOffset = (int)(_position % PAGE_SIZE);
        int currentLength = PAGE_SIZE - currentOffset;

        long startPosition = _position;

        AllocSpaceIfNeeded(_position + count);

        while (count != 0)
        {
            if (currentLength > count)
                currentLength = count;

            Array.Copy(buffer, offset, _streamBuffers[currentPage++], currentOffset, currentLength);

            offset += currentLength;
            _position += currentLength;
            count -= currentLength;

            currentOffset = 0;
            currentLength = PAGE_SIZE;
        }
    }

    #endregion Stream
}

2- 或仅通过 return 将文件从 HDD 推送到客户端(如果您想要更快的传输使用更好的存储单元或将它们移动到超快的 RAM,这可能会很慢。 .) 通过控制器和视图。 使用这个特定的 return 类型:

return new PhysicalFileResult("Directory Containing File", 
"application/octet-stream") 
{ FileDownloadName = "Your file name + extension, for example: test.txt or test.zip etc.." };

这很痛苦,但值得,因为没有人真正在线回答这个问题:)