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

ASP.NET Core 7 Web API服务器端文件下载进度监控与活跃传输统计问询

1. 服务器端监控文件下载进度

要实现进度监控,核心是包装原始文件流,在每次读取字节时记录已传输量,具体实现步骤如下:

  • 自定义继承自Stream的包装类,重写Read方法,累加已传输字节数并输出进度信息(可写入日志、存入监控缓存等)。
  • 在控制器中替换原始FileStream为自定义流。

自定义进度跟踪流代码

public class ProgressTrackingStream : Stream
{
    private readonly Stream _originalStream;
    private readonly string _fileName;
    private long _bytesTransferred;
    private readonly long _totalBytes;

    public ProgressTrackingStream(Stream originalStream, string fileName)
    {
        _originalStream = originalStream;
        _fileName = fileName;
        _totalBytes = originalStream.Length;
        _bytesTransferred = 0;
    }

    public override bool CanRead => _originalStream.CanRead;
    public override bool CanSeek => _originalStream.CanSeek;
    public override bool CanWrite => _originalStream.CanWrite;
    public override long Length => _originalStream.Length;
    public override long Position 
    { 
        get => _originalStream.Position; 
        set => _originalStream.Position = value; 
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int bytesRead = _originalStream.Read(buffer, offset, count);
        if (bytesRead > 0)
        {
            Interlocked.Add(ref _bytesTransferred, bytesRead);
            // 输出进度,可根据需求替换为日志写入或监控上报
            double progress = (double)_bytesTransferred / _totalBytes * 100;
            Console.WriteLine($"文件 {_fileName} 下载进度: {progress:F2}%,已传输 {_bytesTransferred / 1024 / 1024:F2}MB / {_totalBytes / 1024 / 1024:F2}MB");
        }
        return bytesRead;
    }

    public override void Flush() => _originalStream.Flush();
    public override long Seek(long offset, SeekOrigin origin) => _originalStream.Seek(offset, origin);
    public override void SetLength(long value) => _originalStream.SetLength(value);
    public override void Write(byte[] buffer, int offset, int count) => _originalStream.Write(buffer, offset, count);

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _originalStream.Dispose();
            Console.WriteLine($"文件 {_fileName} 下载结束,总传输 {_bytesTransferred / 1024 / 1024:F2}MB");
        }
        base.Dispose(disposing);
    }
}

修改控制器接口

public IActionResult GetFile(string fileName)
{
    try
    {
        if (System.IO.File.Exists(fileName))
        {
            var originalStream = new FileStream(fileName, FileMode.Open);
            var progressStream = new ProgressTrackingStream(originalStream, fileName);
            return new FileStreamResult(progressStream, "application/octet-stream");
        }
                
        var retMessage = $"文件 {fileName} 不存在";
        Console.WriteLine(retMessage);
        return NotFound(retMessage);
    }
    catch (Exception ex)
    {
        Console.WriteLine($"下载出错: {ex.Message}");
        return StatusCode(StatusCodes.Status500InternalServerError);
    }
}
2. 统计当前活跃的客户端文件传输数量

通过线程安全计数器实现,在传输开始时递增计数,传输结束(含成功、失败、客户端中断)时递减计数,结合自定义流的生命周期处理:

定义全局线程安全计数器

public static class TransferCounter
{
    private static int _activeTransfers;

    public static int ActiveTransfers => _activeTransfers;

    public static void Increment()
    {
        Interlocked.Increment(ref _activeTransfers);
        Console.WriteLine($"活跃传输数增加,当前: {_activeTransfers}");
    }

    public static void Decrement()
    {
        Interlocked.Decrement(ref _activeTransfers);
        Console.WriteLine($"活跃传输数减少,当前: {_activeTransfers}");
    }
}

更新自定义流的构造与销毁逻辑

public ProgressTrackingStream(Stream originalStream, string fileName)
{
    _originalStream = originalStream;
    _fileName = fileName;
    _totalBytes = originalStream.Length;
    _bytesTransferred = 0;
    // 传输开始时递增计数
    TransferCounter.Increment();
}

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        _originalStream.Dispose();
        Console.WriteLine($"文件 {_fileName} 下载结束,总传输 {_bytesTransferred / 1024 / 1024:F2}MB");
        // 传输结束时递减计数
        TransferCounter.Decrement();
    }
    base.Dispose(disposing);
}

新增查询活跃传输数的接口

[HttpGet("active-transfers")]
public IActionResult GetActiveTransfers()
{
    return Ok(new { ActiveTransfers = TransferCounter.ActiveTransfers });
}

关键注意事项

  • 线程安全:必须使用Interlocked或Concurrent系列类型,避免多线程环境下的计数错误。
  • 异常兼容:即使下载过程抛出异常,Stream的Dispose方法仍会被FileStreamResult自动调用,确保计数正确递减。
  • 性能影响:自定义流仅增加简单计数逻辑,不会对大文件传输性能造成明显影响。

内容的提问来源于stack exchange,提问作者Angelo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 11:23:16