最直接的本地文件上传方式是AwsS3Client.PutObjectAsync,适用于≤100MB文件;大文件需用TransferUtility.UploadAsync分块上传;务必配置正确IAM权限(含s3:ListBucket)并准确设置ContentType与ContentLength。

用 AwsS3Client.PutObjectAsync 上传本地文件最直接
只要已配置好 AWS 凭据(如 ~/.aws/credentials 或环境变量),且目标存储桶存在、有写权限,PutObjectAsync 就是最轻量的上传方式。它适合小到中等尺寸文件(建议 ≤100MB),不需分片或断点续传。
关键点:
-
BucketName必须全小写、符合 DNS 命名规则(不能含下划线、大写字母) -
Key是 S3 中的对象路径,支持子目录形式(如"uploads/photo.jpg"),但不是真实文件夹 - 务必设置
ContentType,否则 S3 默认为binary/octet-stream,浏览器可能无法正确打开图片/PDF - 上传大文件时会阻塞线程,建议用
await配合CancellationToken
var client = new AmazonS3Client(RegionEndpoint.USEast1);
await client.PutObjectAsync(new PutObjectRequest
{
BucketName = "my-bucket",
Key = "docs/report.pdf",
FilePath = @"C:\temp\report.pdf",
ContentType = "application/pdf"
});
大文件(>100MB)必须用 TransferUtility.UploadAsync
PutObjectAsync 把整个文件读入内存再发请求,超大文件容易 OOM 或超时。TransferUtility 自动分块、并发上传、重试失败块,是官方推荐的大文件方案。
注意:
- 需要显式引用
AWS.Extensions.S3.TransferNuGet 包(新版 SDK 中已拆出) - 默认并发数是 5,可通过
TransferUtilityConfig调整,但过高可能触发 S3 请求限频 - 不支持直接传
Stream,必须是磁盘路径或MemoryStream(后者仍占内存) - 上传进度回调需通过
UploadProgressEvent订阅,不是返回值里带
var config = new TransferUtilityConfig { ConcurrentServiceRequests = 3 };
var utility = new TransferUtility(new AmazonS3Client(RegionEndpoint.USEast1), config);
var fileTransfer = utility.UploadAsync(@"C:\big\video.mp4", "my-bucket", "videos/2024.mp4");
fileTransfer.UploadProgressEvent += (sender, e) =>
Console.WriteLine($"Uploaded {e.TransferredBytes}/{e.TotalBytes}");
await fileTransfer;
上传前必须检查存储桶策略和 IAM 权限
即使代码完全正确,上传失败大概率是权限问题。常见错误信息如:Amazon.S3.AmazonS3Exception: Access Denied 或 NotFound(其实是没权限查桶是否存在)。
最小必要权限示例(IAM Policy):
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
},
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": "arn:aws:s3:::my-bucket"
}
]
}
别漏掉 s3:ListBucket —— TransferUtility 初始化时会先检查桶是否存在,没这个权限就直接抛异常。
流式上传(如从 HTTP 请求体直传)要用 PutObjectAsync + Stream
如果文件来自 API 请求体、数据库 BLOB 或内存生成,不想落地磁盘,就得传 Stream。这时不能用 FilePath,必须手动设 InputStream 和 ContentLength。
注意:
-
ContentLength必须准确,S3 不接受 chunked 编码;若 Stream 不支持Length(如Request.Body),需先缓冲或计算长度 - 确保 Stream 未被提前读取过(Position=0),否则上传空内容
- 不要在上传后立即
DisposeStream,SDK 内部会处理
using var stream = new MemoryStream(data);
await client.PutObjectAsync(new PutObjectRequest
{
BucketName = "my-bucket",
Key = "tmp/data.json",
InputStream = stream,
ContentLength = stream.Length,
ContentType = "application/json"
});
实际项目里最容易卡住的,是权限配置和 ContentLength 漏设 —— 尤其流式场景下,错误提示不明确,得盯住 WireShark 或启用 SDK 日志才能定位。


















