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

如何从ASP.NET Core Web API端点直接上传大文件至Amazon S3?

直接通过ASP.NET Core WebAPI的Multipart上传将文件传至Amazon S3(无磁盘缓冲)

当然可以实现,核心思路是绕开IFormFile的默认磁盘缓冲机制,直接解析请求的Multipart流,并将文件分片直接上传到S3的分段上传接口,全程不将整个文件写入磁盘或加载到内存。

步骤1:禁用ASP.NET Core的表单文件缓冲

默认情况下,IFormFile会将文件缓冲到磁盘或内存,因此需要先在配置中禁用该行为:

// 在Program.cs中配置
builder.Services.Configure<FormOptions>(options =>
{
    // 禁用请求体整体缓冲
    options.BufferBody = false;
    // 彻底禁用内存缓冲,确保文件流直接通过请求流传递
    options.MemoryBufferThreshold = 0;
});

步骤2:直接处理Multipart请求流并上传到S3

不需要依赖IFormFile,直接读取请求的原始流,解析出文件部分后分块上传到S3:

using Amazon.S3;
using Amazon.S3.Model;
using Microsoft.AspNetCore.WebUtilities;

[ApiController]
[Route("api/[controller]")]
public class UploadController : ControllerBase
{
    private readonly IAmazonS3 _s3Client;
    private const string BucketName = "your-target-bucket";
    // S3要求分片(除最后一片)最小5MB
    private const int ChunkSize = 5 * 1024 * 1024;

    public UploadController(IAmazonS3 s3Client)
    {
        _s3Client = s3Client;
    }

    [HttpPost("s3-direct")]
    public async Task<IActionResult> DirectUploadToS3()
    {
        if (!Request.ContentType?.StartsWith("multipart/form-data", StringComparison.OrdinalIgnoreCase) ?? false)
        {
            return BadRequest("仅支持multipart/form-data类型请求");
        }

        var boundary = Request.GetMultipartBoundary();
        var multipartReader = new MultipartReader(boundary, Request.Body);
        MultipartSection section;
        
        // 生成S3对象的唯一键
        var objectKey = Guid.NewGuid().ToString();
        // 初始化S3分段上传
        var initResponse = await _s3Client.InitiateMultipartUploadAsync(new InitiateMultipartUploadRequest
        {
            BucketName = BucketName,
            Key = objectKey
        });

        var partResponses = new List<UploadPartResponse>();
        int partNumber = 1;

        try
        {
            while ((section = await multipartReader.ReadNextSectionAsync()) != null)
            {
                // 跳过非文件的表单字段
                if (string.IsNullOrEmpty(section.FileName))
                {
                    await section.Body.CopyToAsync(Stream.Null);
                    continue;
                }

                // 分块读取文件流并上传到S3
                byte[] buffer = new byte[ChunkSize];
                int bytesRead;
                while ((bytesRead = await section.Body.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    using var chunkStream = new MemoryStream(buffer, 0, bytesRead);
                    var uploadRequest = new UploadPartRequest
                    {
                        BucketName = BucketName,
                        Key = objectKey,
                        UploadId = initResponse.UploadId,
                        PartNumber = partNumber++,
                        InputStream = chunkStream
                    };
                    var partResponse = await _s3Client.UploadPartAsync(uploadRequest);
                    partResponses.Add(partResponse);
                }
            }

            // 完成S3分段上传
            await _s3Client.CompleteMultipartUploadAsync(new CompleteMultipartUploadRequest
            {
                BucketName = BucketName,
                Key = objectKey,
                UploadId = initResponse.UploadId,
                PartETags = partResponses.Select(p => new PartETag(p.PartNumber, p.ETag)).ToList()
            });

            return Ok(new { ObjectKey = objectKey, Bucket = BucketName });
        }
        catch (Exception ex)
        {
            // 上传失败时中止S3分段上传,避免残留分片占用空间
            await _s3Client.AbortMultipartUploadAsync(new AbortMultipartUploadRequest
            {
                BucketName = BucketName,
                Key = objectKey,
                UploadId = initResponse.UploadId
            });
            return StatusCode(500, $"上传失败:{ex.Message}");
        }
    }
}

关键注意事项

  • 必须确保请求流未被缓冲:配置FormOptions是核心,否则ASP.NET Core依然会将文件写入磁盘。
  • 分片大小限制:S3要求除最后一片外,每个分片至少5MB,代码中设置的ChunkSize需符合该规则。
  • 异常处理:上传失败时一定要调用AbortMultipartUploadAsync,否则未完成的分片会持续占用S3存储空间并产生费用。
  • 表单字段处理:非文件的表单字段需要将流读取到Stream.Null,避免阻塞请求流解析。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 19:45:34