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

C#实现Minio S3服务器上传速率限制方案咨询

Minio S3上传速率限制解决方案

要解决上传时占满带宽的问题,最直接的方式是包装文件流并限制读写速度,以下是具体实现方案:

1. 实现限速流类

自定义一个ThrottledStream类,继承自Stream,通过控制每秒传输的字节数来限制带宽。这里提供异步版本的实现,避免阻塞线程:

public class ThrottledStream : Stream
{
    private readonly Stream _baseStream;
    private readonly long _maxBytesPerSecond;
    private long _bytesTransferredInSecond;
    private DateTime _lastSecondReset;

    public ThrottledStream(Stream baseStream, long maxBytesPerSecond)
    {
        _baseStream = baseStream ?? throw new ArgumentNullException(nameof(baseStream));
        _maxBytesPerSecond = maxBytesPerSecond > 0 ? maxBytesPerSecond : throw new ArgumentOutOfRangeException(nameof(maxBytesPerSecond));
        _lastSecondReset = 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 int Read(byte[] buffer, int offset, int count) => throw new NotImplementedException("请使用异步ReadAsync方法");

    public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        await ThrottleAsync(count, cancellationToken);
        return await _baseStream.ReadAsync(buffer, offset, count, cancellationToken);
    }

    public override void Write(byte[] buffer, int offset, int count) => throw new NotImplementedException("请使用异步WriteAsync方法");

    public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
    {
        await ThrottleAsync(count, cancellationToken);
        await _baseStream.WriteAsync(buffer, offset, count, cancellationToken);
    }

    private async Task ThrottleAsync(int bytesToTransfer, CancellationToken cancellationToken)
    {
        var now = DateTime.UtcNow;
        var elapsed = now - _lastSecondReset;

        // 每秒重置已传输字节数
        if (elapsed.TotalSeconds >= 1)
        {
            _bytesTransferredInSecond = 0;
            _lastSecondReset = now;
        }

        _bytesTransferredInSecond += bytesToTransfer;
        // 如果超过每秒限制,计算需要延迟的时间
        if (_bytesTransferredInSecond > _maxBytesPerSecond)
        {
            var excessBytes = _bytesTransferredInSecond - _maxBytesPerSecond;
            var delay = TimeSpan.FromSeconds(excessBytes / (double)_maxBytesPerSecond);
            await Task.Delay(delay, cancellationToken);
            // 重置已传输字节数到上限,避免重复计算
            _bytesTransferredInSecond = _maxBytesPerSecond;
        }
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _baseStream.Dispose();
        }
        base.Dispose(disposing);
    }
}

2. 修改上传代码

用ThrottledStream包装文件流,指定每秒最大传输字节数(比如限制为1MB/s,即1024*1024字节):

// 设置限速:1MB/s
var maxBytesPerSecond = 1024 * 1024;

using var fileStream = File.OpenRead(filePath);
using var throttledStream = new ThrottledStream(fileStream, maxBytesPerSecond);

// 替换原有的PutObjectAsync调用,传入限速流
await s3.PutObjectAsync(bucketName, objectName, throttledStream, contentType);

注意事项

  • 可以根据实际需求调整maxBytesPerSecond的值,比如2MB/s就是2*1024*1024。
  • 该方案不依赖Minio SDK的特定版本,兼容性强,只要是通过流上传的场景都适用。
  • 如果需要更精确的限速,可以优化ThrottledStream中的时间计算逻辑,比如用高精度计时器代替DateTime.UtcNow。

内容的提问来源于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 20:54:22