You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用WebClient或HttpClient将文件分片上传到Amazon S3的实现问题

C# 基于预签名URL实现Amazon S3分片上传逻辑实现

首先定义分片上传结果存储类,用于第四步完成上传时传入S3要求的分片编号+ETag对应列表:

public class PartUploadResult
{
    // S3要求分片编号从1开始计数
    public int PartNumber { get; set; }
    // S3返回的分片ETag值
    public string ETag { get; set; }
}

然后定义分片大小常量,和你要求的5MB一致:

private const int PART_SIZE = 5 * 1024 * 1024;

单分片上传方法实现(基于你现有HttpWebRequest写法适配)

public PartUploadResult UploadSinglePart(string preSignedUrl, string filePath, long partStartPosition, int partSize, int partNumber)
{
    HttpWebRequest httpRequest = WebRequest.Create(preSignedUrl) as HttpWebRequest;
    httpRequest.Method = "PUT";
    // 标记当前分片的内容长度,避免超出分片大小
    httpRequest.ContentLength = partSize;

    using (Stream dataStream = httpRequest.GetRequestStream())
    {
        byte[] buffer = new byte[8192];
        int totalBytesRead = 0;
        using FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
        // 跳转到当前分片的起始读取位置
        fileStream.Seek(partStartPosition, SeekOrigin.Begin);
        
        while (totalBytesRead < partSize)
        {
            int bytesToRead = Math.Min(buffer.Length, partSize - totalBytesRead);
            int bytesRead = fileStream.Read(buffer, 0, bytesToRead);
            if (bytesRead == 0) break;
            dataStream.Write(buffer, 0, bytesRead);
            totalBytesRead += bytesRead;
        }
    }

    using HttpWebResponse response = httpRequest.GetResponse() as HttpWebResponse;
    // S3返回的ETag默认带双引号,需要去除
    string etag = response.Headers["ETag"].Trim('"');
    return new PartUploadResult
    {
        PartNumber = partNumber,
        ETag = etag
    };
}

批量分片上传调用逻辑

你已经生成了各分片对应的预签名URL列表,直接遍历调用即可:

public List<PartUploadResult> UploadAllParts(string filePath, List<string> preSignedUrls)
{
    List<PartUploadResult> uploadResults = new List<PartUploadResult>();
    FileInfo fileInfo = new FileInfo(filePath);
    long fileLength = fileInfo.Length;

    for (int i = 0; i < preSignedUrls.Count; i++)
    {
        // 分片编号从1开始,符合S3要求
        int partNumber = i + 1;
        long partStart = i * PART_SIZE;
        int currentPartSize = (int)Math.Min(PART_SIZE, fileLength - partStart);
        string preSignedUrl = preSignedUrls[i];
        
        var partResult = UploadSinglePart(preSignedUrl, filePath, partStart, currentPartSize, partNumber);
        uploadResults.Add(partResult);
    }

    return uploadResults;
}

拿到uploadResults列表后,直接传给你已经实现的第四步完成分片上传的方法即可。

可选优化

如果你使用的是.NET Core/.NET 5及以上版本,推荐用HttpClient代替HttpWebRequest,性能和可维护性更好,单分片上传参考写法如下:

// 建议全局声明一个HttpClient实例,不要每次请求都新建
private static readonly HttpClient _httpClient = new HttpClient();

public async Task<PartUploadResult> UploadSinglePartAsync(string preSignedUrl, string filePath, long partStartPosition, int partSize, int partNumber)
{
    using FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    fileStream.Seek(partStartPosition, SeekOrigin.Begin);
    // 构造分片内容流
    using var partContent = new StreamContent(fileStream, partSize);
    var response = await _httpClient.PutAsync(preSignedUrl, partContent);
    response.EnsureSuccessStatusCode();
    string etag = response.Headers.ETag.Tag.Trim('"');
    return new PartUploadResult
    {
        PartNumber = partNumber,
        ETag = etag
    };
}

注意事项

  • S3分片编号必须从1开始计数,不要用0作为第一个分片的编号
  • 单个分片最小不能小于5MB(最后一个分片除外),否则完成上传时会报错
  • 如果需要支持断点续传,可将已上传成功的分片结果持久化存储,下次上传时跳过已完成的分片即可

内容的提问来源于stack exchange,提问作者JMR

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.15 15:54:08