如何从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
相关产品推荐
相关产品推荐

