AWS S3作为对象存储服务,在文件备份、静态资源托管和大数据中转等场景里使用频率很高。C#开发者通过AWS SDK for .NET操作S3时,可以选择的上传方式不止一种,简单上传适合小文件,分块上传适合大文件或网络不稳定的环境。上传之前先要完成SDK安装和凭证配置,否则请求会直接失败。

安装与配置 AWS SDK for .NET
在Visual Studio或dotnet CLI项目中,首先要引入S3对应的NuGet包。执行下面的命令即可安装AWSSDK.S3,这个包已经包含了访问S3所需的全部客户端和模型类。
dotnet add package AWSSDK.S3
凭证配置方式比较灵活,常见的有四种。第一种是在用户目录下的.aws\credentials文件中写入AccessKey和SecretKey,SDK会自动读取。第二种是通过环境变量AWS_ACCESS_KEY_ID和AWS_SECRET_ACCESS_KEY传递。第三种是在代码中显式创建BasicAWSCredentials对象,适合测试环境,但不要把密钥硬编码到生产代码里。第四种是运行在EC2或ECS上时,直接给实例或任务绑定IAM角色,SDK会自动获取临时凭证,这是最安全的方式。
初始化客户端时需要指定服务区域,例如亚太区域使用RegionEndpoint.APSoutheast1。区域设置错误会导致请求被路由到错误的S3终端节点,进而出现NoSuchBucket或301重定向异常。下面的代码演示了凭证和区域的基本设置。
using Amazon;
using Amazon.S3;
using Amazon.Runtime;
var credentials = new BasicAWSCredentials("your-access-key", "your-secret-key");
var client = new AmazonS3Client(credentials, RegionEndpoint.APSoutheast1);
如果应用运行在AWS托管环境里,可以直接使用无参构造函数,SDK会从实例元数据中获取角色凭证。这种方式不需要在配置文件中保存任何长期密钥,能显著降低泄露风险。
基础上传操作与关键参数
小文件上传最常用的接口是PutObjectAsync。它接受一个PutObjectRequest对象,需要指定存储桶名称、对象键和文件流。对象键就是文件在S3中的完整路径,例如images/2025/logo.png。文件内容可以通过FileStream读取,也可以直接传入字节数组。
using Amazon.S3;
using Amazon.S3.Model;
public async Task UploadFileAsync(AmazonS3Client client, string bucketName, string key, string filePath)
{
using var fileStream = File.OpenRead(filePath);
var request = new PutObjectRequest
{
BucketName = bucketName,
Key = key,
InputStream = fileStream,
ContentType = "application/octet-stream",
AutoCloseStream = true
};
var response = await client.PutObjectAsync(request);
Console.WriteLine(response.HttpStatusCode);
}
PutObjectRequest还支持很多有用属性。CannedACL可以设置对象访问权限,例如PublicRead让对象可公开访问;StorageClass可以切换为标准、低频或归档存储;Metadata是一个字典,能存储自定义元数据,比如原始文件名或校验值。如果目标文件本身需要被浏览器直接读取,务必设置正确的ContentType,否则下载时会变成附件。
对于来自内存或者网络流的数据,不需要先写入本地临时文件,直接把Stream传给InputStream即可。需要注意,流在使用完毕后会被自动关闭,如果需要复用同一个流,建议先复制到MemoryStream。上传完成后可以通过响应的ETag判断文件是否完整,并在数据库或日志中记录该值以便后续校验。
大文件分块上传与断点续传
当文件超过几十MB时,继续使用简单上传会受到单请求5GB上限的限制,而且网络中断后必须从头再来。这时应该使用TransferUtility工具类,它封装了S3的分块上传API,能够自动进行并发分块、失败重试和进度回调。下面的代码展示了如何通过TransferUtility上传一个大文件,并设置分块大小为16MB。
using Amazon.S3.Transfer;
using Amazon.S3;
var transferUtility = new TransferUtility(client);
var uploadRequest = new TransferUtilityUploadRequest
{
BucketName = bucketName,
Key = key,
FilePath = filePath,
PartSize = 16 * 1024 * 1024,
StorageClass = S3StorageClass.Standard,
UploadProgressEvent = (sender, e) =>
{
Console.WriteLine($"{e.TransferredBytes}/{e.TotalBytes}");
}
};
await transferUtility.UploadAsync(uploadRequest);
TransferUtility会先在内存中把文件切成指定大小的分片,每个分片作为一个独立请求上传。如果某个分片失败,它只会重试该分片而不是整个文件,这就是断点续传的基础。对于超大文件或高并发场景,还可以通过ConcurrentServiceRequests调整同时上传的分片数量,以平衡带宽和内存消耗。
如果需要更细粒度的控制,也可以手动调用底层分块接口。先初始化分块上传拿到UploadId,然后逐个调用UploadPartAsync,最后用CompleteMultipartUploadAsync合并所有分片。这种方式虽然代码量大,但可以在数据库中保存UploadId和已上传分片编号,实现完全自定义的断点恢复逻辑。手动实现时需要自己管理分片大小、重试次数和清理未完成的分块上传,否则会产生存储费用。
常见错误排查与权限安全建议
AccessDenied是最常见的S3错误之一,通常意味着当前凭证没有对应操作权限。检查时先确认IAM策略是否包含s3:PutObject,同时还要确认存储桶策略没有显式拒绝。另一个容易被忽略的地方是KMS加密场景,如果对象使用SSE-KMS加密,还需要kms:GenerateDataKey和kms:Decrypt权限,否则即使有s3:PutObject也会失败。
区域不匹配同样会引发异常。S3存储桶有一个固定的所在区域,如果客户端初始化的区域与存储桶不一致,SDK可能会返回301,并提示需要正确的区域。可以在请求时显式指定BucketRegion,或者用S3Config的ForcePathStyle属性规避部分代理环境下的区域解析问题。遇到此类错误时,查看异常中的StatusCode和RequestId有助于快速定位。
生产环境建议采用最小权限原则,例如只允许上传到指定存储桶和指定前缀,并禁止删除操作。下面是一个IAM策略片段,授予对my-app-bucket存储桶uploads/前缀的PutObject和GetObject权限。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-app-bucket/uploads/*"
}
]
}
凭证管理方面,优先使用IAM角色或临时凭证,避免把长期AccessKey写入代码仓库。如果必须使用静态密钥,定期轮换并配合密钥管理服务进行加密存储。对于公网传输的数据,S3默认使用HTTPS,上传前会进行TLS加密,无需额外配置。
最后,如果应用需要让终端用户直接向S3上传文件,可以考虑生成预签名URL。预签名URL允许用户在限定时间内直接向S3写入对象,不需要经过应用服务器中转,能显著降低带宽压力。SDK中的GetPreSignedURL方法可以生成PUT类型的URL,C#中实现起来也较为简单,适合移动端或前端直传场景。
C# AWS S3AWSSDK 上传文件S3 文件上传修改时间:2026-09-17 20:03:03