.Net Core中Minio PutObjectAsync上传大于12MB文件失败
解决Minio大文件上传签名不匹配问题
问题场景
当前使用以下代码向Minio存储桶保存文件:
public async Task SaveFileAsync(Stream stream, string fileName) { MinioClient client = InitClient(); bool found = await client.BucketExistsAsync(bucketName); if (!found) { await client.MakeBucketAsync(bucketName); } var fileType = "application/octet-stream"; stream.Seek(0, SeekOrigin.Begin); await client.PutObjectAsync(bucketName, fileName, stream, stream.Length, fileType); }
ASP.NET Core控制器中通过MemoryStream传递文件流:
public async Task<IActionResult> UploadAsync(int id, IFormFile file) { var command = new MyApplicationCommand(); using (var memoryStream = new MemoryStream()) { await file.CopyToAsync(memoryStream); command.ContentStream = memoryStream; command.ContentType = file.ContentType; command.FileName = file.FileName; await mediator.Send(command); // 命令处理器调用SaveFileAsync } }
上传大于12MB的文件时,PutObjectAsync返回错误:
MinIO API responded with message=The request signature we calculated does not match the signature you provided. Check your key and signing method.
堆栈跟踪信息:
at Minio.MinioClient.ParseError(IRestResponse response) in /home/maxim/dev/minio-dotnet/Minio/MinioClient.cs:line 491 at Minio.MinioClient.<>c.<.ctor>b__81_0(IRestResponse response) in /home/maxim/dev/minio-dotnet/Minio/MinioClient.cs:line 75 at Minio.MinioClient.HandleIfErrorResponse(IRestResponse response, IEnumerable`1 handlers, DateTime startTime) in /home/maxim/dev/minio-dotnet/Minio/MinioClient.cs:line 525 at Minio.MinioClient.ExecuteTaskAsync(IEnumerable`1 errorHandlers, IRestRequest request, CancellationToken cancellationToken) in /home/maxim/dev/minio-dotnet/Minio/MinioClient.cs:line 371 at Minio.MinioClient.PutObjectAsync(String bucketName, String objectName, String uploadId, Int32 partNumber, Byte[] data, Dictionary`2 metaData, Dictionary`2 sseHeaders, CancellationToken cancellationToken) in /home/maxim/dev/minio-dotnet/Minio/ApiEndpoints/ObjectOperations.cs:line 497 at Minio.MinioClient.PutObjectAsync(String bucketName, String objectName, Stream data, Int64 size, String contentType, Dictionary`2 metaData, ServerSideEncryption sse, CancellationToken cancellationToken) in /home/maxim/dev/minio-dotnet/Minio/ApiEndpoints/ObjectOperations.cs:line 301
核心原因
Minio对大于默认分片阈值(通常10MB)的文件会自动启用分片上传。此时如果流的状态异常(如被提前释放、长度与实际读取内容不符),会导致每片请求的签名计算与服务器端不一致,触发错误。
解决步骤
1. 避免提前释放MemoryStream
控制器中的using块会在mediator.Send完成后立即释放MemoryStream,但分片上传是异步进行的,后续分片请求读取流时会失败,导致签名错误。修改控制器代码,确保流在上传完成后再释放:
public async Task<IActionResult> UploadAsync(int id, IFormFile file) { var command = new MyApplicationCommand(); var memoryStream = new MemoryStream(); await file.CopyToAsync(memoryStream); command.ContentStream = memoryStream; command.ContentType = file.ContentType; command.FileName = file.FileName; try { await mediator.Send(command); } finally { memoryStream.Dispose(); // 上传完成后再释放流 } return Ok(); }
2. 直接使用IFormFile的原始流(推荐)
没必要将文件复制到MemoryStream,直接使用IFormFile.OpenReadStream()可以减少内存占用,同时避免流状态问题:
public async Task<IActionResult> UploadAsync(int id, IFormFile file) { var command = new MyApplicationCommand(); command.ContentStream = file.OpenReadStream(); command.ContentType = file.ContentType; command.FileName = file.FileName; await mediator.Send(command); return Ok(); }
同时修改SaveFileAsync,使用传入的ContentType而非硬编码:
public async Task SaveFileAsync(Stream stream, string fileName, string contentType) { MinioClient client = InitClient(); bool found = await client.BucketExistsAsync(bucketName); if (!found) { await client.MakeBucketAsync(bucketName); } stream.Seek(0, SeekOrigin.Begin); await client.PutObjectAsync(bucketName, fileName, stream, stream.Length, contentType); }
3. 验证Minio客户端版本
旧版本的Minio .NET SDK可能存在分片上传的签名计算bug,升级到最新稳定版(通过NuGet更新Minio包)。
4. 检查客户端配置
确保InitClient()中配置的:
- 访问密钥(Access Key)和秘密密钥(Secret Key)正确
- Minio服务器端点(Endpoint)正确,包含协议(http/https)和端口
- 区域(Region)与Minio服务器配置一致(如果服务器设置了区域)
内容的提问来源于stack exchange,提问作者dawid
相关产品推荐
相关产品推荐

