与 WebClient 并行上传文件

问题描述

我需要在项目中使用 WebClient 将文件拆分为多个部分并并行上传。到目前为止,我可以一次上传一个部分,但不确定如何并行上传它们。

我有一个 UploadPart 方法,如下所示:

private async Task<PartETag> UploadPart(string filePath,string preSignedUrl,int partNumber)
{
    WebClient wc = new();
    wc.UploadProgressChanged += WebClientUploadProgressChanged;
    wc.UploadFileCompleted += WebClientUploadCompleted;
    _ = await wc.UploadFileTaskAsync(new Uri(preSignedUrl),"PUT",filePath);

    // Obtain the WebHeaderCollection instance containing the header name/value pair from the response.
    WebHeaderCollection myWebHeaderCollection = wc.ResponseHeaders;
    string formattedETag = myWebHeaderCollection.GetValues("ETag").FirstOrDefault().Replace(@"""","");
    PartETag partETag = new(partNumber,formattedETag);

    return partETag;
}

在 foreach 循环中调用

foreach (var part in parts)
{
    var partETag = await UploadPart(part.FilePath,part.PresignedUrl,part.Number);
    partETags.Add(partETag);
}

如何修改它以便我并行上传部分(一次最多 10 个部分),同时仍返回响应标头中的 PartETag 值?

解决方法

这是TPL Dataflow的完美场景:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;

var parts = new List<Part>();
var partEtags = new List<PartETag>();

var transformBlock = new TransformBlock<Part,PartETag>
(
    async part => await UploadPart(part.FilePath,part.PreSignedUrl,part.PartNumber),new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 10}
);

var actionBlock = new ActionBlock<PartETag>(partETag => partEtags.Add(partETag));

transformBlock.LinkTo(actionBlock,new DataflowLinkOptions {PropagateCompletion = true});

foreach (Part part in parts)
{
    transformBlock.Post(part);
}

transformBlock.Complete();

await actionBlock.Completion;

我对你的类做了一些假设,因为你没有展示你的所有代码。顶部的 parts 列表显然需要在其中包含实例。

此代码创建了一个数据流,该数据流以异步方式执行工作并将并行执行次数限制为 10。这些块与完成传播相关联,因此我们等待操作块的完成以确保一切都完成。

完成后,您的 partEtags 列表将包含您的所有结果。