You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 21:57:41