在 ASP.NET Core 中实现下载大于 2gb 的大文件

5 c# download .net-core 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);
Run Code Online (Sandbox Code Playgroud)

但是,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-问题?论坛=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");
Run Code Online (Sandbox Code Playgroud)

给出了此异常:System.IO.IOException:'文件太长。此操作目前仅限于支持大小小于 2 GB 的文件。

我不知道该怎么办了..有什么建议吗?最后我试图返回 File(byte[]) 这样我就可以从服务器下载该文件。

小智 7

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

1-创建一个将 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
}
Run Code Online (Sandbox Code Playgroud)

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

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

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