【问题标题】:Parallel Chunked vs Sequential Chunked file upload并行分块与顺序分块文件上传
【发布时间】:2023-03-15 03:02:01
【问题描述】:

我正在尝试找出将大文件上传到服务器的最佳方式。在我的本地,我尝试了两种方法:

  1. 顺序分块上传,其中文件被分解为多个块并一次上传一个到服务器。在服务器端,传入的块直接附加到最终文件中。
  2. 并行块上传,其中块并行上传到服务器。服务端将文件存储为临时小文件,等所有chunk上传完成后合并为一个完整的文件。

但是与上传时间相比,这里的合并时间要长得多,至少在我的本地。因此,并行上传总是比顺序上传慢很多。如何缩短上传时间?

这是顺序上传代码:

    [HttpPost]
    [RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
    [RequestSizeLimit(209715200)]
    public async Task<IActionResult> UploadSeq()
    {
        string filename = "./Uploads/seqfile";
        using(Stream file = Request.Body)
        using (FileStream stream = new FileStream(filename, FileMode.Append))
        {
            await file.CopyToAsync(stream);
            await stream.FlushAsync();
        }
        return Ok();
    }

这是并行代码:

    [HttpPost("/ParUpload/{id}/{size}")]
    [RequestFormLimits(MultipartBodyLengthLimit = 209715200)]
    [RequestSizeLimit(209715200)]
    public async Task<IActionResult> UploadParallel(int id, int size)
    {
        string filename = "./Uploads/parfilecomplete.mkv";
        using (Stream file = Request.Body)
        using (FileStream stream = new FileStream(filename, FileMode.OpenOrCreate, FileAccess.Write, FileShare.Write))
        {
            stream.Seek(id * size, SeekOrigin.Begin);
            await file.CopyToAsync(stream);
            await stream.FlushAsync();
        }
        return Ok();
    }

【问题讨论】:

  • 是的,你可以。 Torrent 协议就像一个快速的例子。直接将块写入目标文件中的位置。限制导致重用套接字的并发上传数量,这将避免建立大量新的 TCP 连接。
  • 我也尝试过这种方法,但仍然比顺序方法慢很多..
  • 请在两边显示两个实现的代码。不能再慢了。
  • 取决于硬件(真实磁盘与 SSD 的寻道成本不一样)。还尝试删除刷新调用。 Windows 也有 WriteFileGather 但没有暴露给 .NET。 docs.microsoft.com/en-us/windows/win32/api/fileapi/… 不确定它会添加什么。您应该发布重现的服务器和客户端测试代码。魔鬼藏在细节里。

标签: c# .net asp.net-mvc file


【解决方案1】:

对于大文件,首选并行上传。但是要考虑的事情很少

  1. 将文件分成许多小文件时,请确保不要一次上传所有文件。确保最多有约 50 个并行上传。当然,这个数字可能会有所不同,但如果您尝试一次上传 10000 个文件,肯定不会很好。使用某种活动上传队列。
  2. 确保服务器支持并行下载。

【讨论】:

    【解决方案2】:

    并行取胜,但如果你想开发这样的系统,你必须做额外的工作。

    1. 您不应为每个域 (FQDN) 创建超过 6 个连接,因为 客户端 端存在连接限制。你可以通过使用相同的ip但不同的域名来增加限制,如upload1.domain.comupload2.domain.com等。但是你可能会填满带宽。

    2. 您不应该在每次收到零件时打开和关闭文件,这是一项非常昂贵的操作。而是创建一个打开文件的写入器类,然后将接收到的部分和要写入的位置排队,写入器将在可以写入时写入,并在写入所有部分时关闭文件。您可以用内存来交换磁盘延迟。

    3. 如果可以启用 TCP 快速启动。这将使您更快地将数据发送到您的服务器

    4. 明智地选择缓冲区大小。尝试不同的大小,它需要最少的磁盘 IO,甚至更少的网络 IO。缓冲区大小必须是磁盘扇区大小的倍数。

    这些是我从之前的经历中想到的。我不记得确切的细节,但这些物品应该会有所帮助。

    【讨论】:

    • 2.我无法得到你。我如何将编写器保存在 .NET 核心的内存中?
    • @PrakharLondhe 我不知道,可以尝试创建一个单例来保存文件写入器。
    猜你喜欢
    • 1970-01-01
    • 2018-09-30
    • 2017-01-08
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    • 2016-02-04
    • 2011-12-12
    • 2018-01-20
    相关资源
    最近更新 更多