如何在C#中使用AWS S3 SDK限制文件上传速度?
在C# AWS S3 SDK中限制上传带宽的实现方案
当然可以实现,AWS S3 .NET SDK本身没有直接提供带宽限制的API,但我们可以通过包装数据流的方式,控制SDK读取数据的速度,从而间接限制上传带宽。
首先要注意:你的当前代码用ContentBody传递500MB-1GB的大文件,会把整个文件加载到内存里,这会导致严重的内存占用问题,建议改用InputStream来流式上传,同时结合限速流控制速度。
步骤1:实现限速包装流
创建一个ThrottledStream类,继承自Stream,通过控制每次读取的字节数和时间间隔来限制传输速度:
public class ThrottledStream : Stream { private readonly Stream _baseStream; private readonly long _maxBytesPerSecond; private long _bytesReadInCurrentSecond; private DateTime _currentSecondStart; public ThrottledStream(Stream baseStream, long maxBytesPerSecond) { _baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream)); _maxBytesPerSecond = maxBytesPerSecond > 0 ? maxBytesPerSecond : throw new ArgumentOutOfRangeException(nameof(maxBytesPerSecond)); _currentSecondStart = DateTime.UtcNow; } public override bool CanRead => _baseStream.CanRead; public override bool CanSeek => _baseStream.CanSeek; public override bool CanWrite => _baseStream.CanWrite; public override long Length => _baseStream.Length; public override long Position { get => _baseStream.Position; set => _baseStream.Position = value; } public override void Flush() => _baseStream.Flush(); public override long Seek(long offset, SeekOrigin origin) => _baseStream.Seek(offset, origin); public override void SetLength(long value) => _baseStream.SetLength(value); public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { var now = DateTime.UtcNow; var elapsed = now - _currentSecondStart; if (elapsed.TotalSeconds >= 1) { _bytesReadInCurrentSecond = 0; _currentSecondStart = now; } var remainingBytesInSecond = _maxBytesPerSecond - _bytesReadInCurrentSecond; var bytesToRead = (int)Math.Min(count, remainingBytesInSecond); if (bytesToRead == 0) { var waitTime = TimeSpan.FromSeconds(1) - elapsed; await Task.Delay(waitTime, cancellationToken); return await ReadAsync(buffer, offset, count, cancellationToken); } var bytesRead = await _baseStream.ReadAsync(buffer, offset, bytesToRead, cancellationToken); _bytesReadInCurrentSecond += bytesRead; return bytesRead; } public override int Read(byte[] buffer, int offset, int count) { var now = DateTime.UtcNow; var elapsed = now - _currentSecondStart; if (elapsed.TotalSeconds >= 1) { _bytesReadInCurrentSecond = 0; _currentSecondStart = now; } var remainingBytesInSecond = _maxBytesPerSecond - _bytesReadInCurrentSecond; var bytesToRead = (int)Math.Min(count, remainingBytesInSecond); if (bytesToRead == 0) { var waitTime = TimeSpan.FromSeconds(1) - elapsed; Thread.Sleep(waitTime); return Read(buffer, offset, count); } var bytesRead = _baseStream.Read(buffer, offset, bytesToRead); _bytesReadInCurrentSecond += bytesRead; return bytesRead; } public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException("ThrottledStream for upload should be read-only"); public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) => throw new NotSupportedException("ThrottledStream for upload should be read-only"); }
步骤2:修改上传代码,使用限速流
把原代码中的ContentBody替换为InputStream,并用ThrottledStream包装,同时调整大文件的处理方式(避免用字符串存储大文件):
// 传入文件路径而非字符串,避免大文件内存溢出 public async Task UploadDataFileAsync(string filePath, string storageKeyId, string storageSecret, string region, long maxBytesPerSecond) { var regionIdentifier = RegionEndpoint.GetBySystemName(region); using (var client = new AmazonS3Client(storageKeyId, storageSecret, regionIdentifier)) { try { using (var fileStream = File.OpenRead(filePath)) using (var throttledStream = new ThrottledStream(fileStream, maxBytesPerSecond)) { var putRequest = new PutObjectRequest { BucketName = AwsBucketName, Key = AwsBucketFileName, InputStream = throttledStream, ContentType = "application/json" }; await client.PutObjectAsync(putRequest); } } catch (AmazonS3Exception amazonS3Exception) { if (amazonS3Exception.ErrorCode != null && (amazonS3Exception.ErrorCode.Equals("InvalidAccessKeyId") || amazonS3Exception.ErrorCode.Equals("InvalidSecurity"))) { throw new SecurityException("无效的Amazon S3凭证 - 数据未上传。", amazonS3Exception); } throw new HttpRequestException("上传数据时发生未指定错误:" + amazonS3Exception.Message, amazonS3Exception); } // 注意:大文件上传后不要调用GetObjectAsync读取整个内容,会占用大量内存,仅小文件测试时使用 // var response = await client.GetObjectAsync(AwsBucketName, AwsBucketFileName); // using (var reader = new StreamReader(response.ResponseStream)) // { // Debug.WriteLine(await reader.ReadToEndAsync()); // } } }
关键说明
maxBytesPerSecond是每秒允许传输的最大字节数,比如要限制为1MB/s,就传入1024 * 1024- 核心逻辑是通过控制SDK读取数据的速度,间接限制上传带宽——SDK的上传速度完全依赖于从
InputStream读取数据的速度 - 必须改用
InputStream流式处理大文件,这是避免内存溢出的最佳实践
内容的提问来源于stack exchange,提问作者Mauri
相关产品推荐
相关产品推荐

