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
相关产品推荐
相关产品推荐

