【问题标题】:Implement Downloading Large files > 2gb in ASP.NET Core在 ASP.NET Core 中实现下载大于 2gb 的大文件
【发布时间】:2021-08-02 17:26:23
【问题描述】:

这是我的代码:

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: '文件太长。此操作目前仅限于支持大小小于 2 GB 的文件。'

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

【问题讨论】:

  • @Genusatplay 谢谢,不幸的是我已经研究过了,在那里找不到有用的代码,只是到处乱说+他甚至没有完成他创建的所谓方法作为示例.net 核心 ...
  • 这篇文章对下载限制有很好的解释,您可以参考以下内容,可能对您有帮助。stackoverflow.com/questions/62502286/…
  • @Chaodeng 谢谢,我已经查看了所有github帖子,这一篇再次说明了要做什么,没有解释或参考如何使用给出的信息,我已经尝试过我自己的研究,但找不到任何可能的解决方法,所以如果你能做一个你认为有效的代码示例,那就随意吧!
  • @AhmedAyman public async Task<IActionResult> GetFile() { ... return File(stream, "application/octet-stream"); }你也试试这个?

标签: c# asp.net-core .net-core download


【解决方案1】:

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

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
}

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.." };

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 你能喜欢显示小示例或编辑代码并显示下载 IActionResult 方法(控制器)的示例,以便我能更了解它的用法吗?
    猜你喜欢
    • 1970-01-01
    • 2013-02-19
    • 2013-05-07
    • 2013-10-19
    • 2021-03-15
    • 1970-01-01
    • 2022-02-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多