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

ASP.NET Core静态文件服务器如何监控文件下载进度?

ASP.NET Core服务端监控文件下载进度方案

方案1:自定义文件传输中间件(推荐)

直接替换默认静态文件中间件,手动处理文件读取与传输,全程监控字节发送量,适配你的静态文件分发场景。

  • 定义线程安全的进度跟踪类,用于在中间件和GUI线程间共享进度数据
  • 编写自定义中间件,处理文件请求、分块传输并更新进度
  • 在GUI中监听进度对象,实时更新UI

1. 进度跟踪类实现

public class DownloadProgressTracker
{
    public string FilePath { get; set; }
    public long TotalBytes { get; set; }
    public long SentBytes { get; private set; }
    public double Progress => TotalBytes == 0 ? 0 : (double)SentBytes / TotalBytes * 100;

    private readonly object _lock = new object();

    public void AddSentBytes(long bytes)
    {
        lock (_lock)
        {
            SentBytes += bytes;
        }
    }

    public void Reset()
    {
        lock (_lock)
        {
            SentBytes = 0;
            TotalBytes = 0;
        }
    }
}

2. 注册进度跟踪对象并替换中间件

private DownloadProgressTracker _progressTracker;

private void RunServer(string configPath)
{
    _progressTracker = new DownloadProgressTracker();
    var builder = WebApplication.CreateBuilder();
    builder.Services.AddSingleton(_progressTracker);
    var app = builder.Build();

    // 替换默认静态文件中间件为自定义实现
    app.UseMiddleware<CustomFileTransferMiddleware>(configPath);

    app.Run();
}

3. 自定义文件传输中间件

public class CustomFileTransferMiddleware
{
    private readonly RequestDelegate _next;
    private readonly string _configPath;
    private readonly DownloadProgressTracker _progressTracker;

    public CustomFileTransferMiddleware(RequestDelegate next, string configPath, DownloadProgressTracker progressTracker)
    {
        _next = next;
        _configPath = configPath;
        _progressTracker = progressTracker;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        var filePath = Path.Combine(_configPath, context.Request.Path.Value.TrimStart('/'));
        if (!File.Exists(filePath))
        {
            await _next(context);
            return;
        }

        var fileInfo = new FileInfo(filePath);
        _progressTracker.Reset();
        _progressTracker.FilePath = filePath;
        _progressTracker.TotalBytes = fileInfo.Length;

        context.Response.ContentType = GetContentType(filePath);
        context.Response.ContentLength = fileInfo.Length;

        using var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
        var buffer = new byte[8192];
        int bytesRead;

        while ((bytesRead = await fileStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
        {
            await context.Response.Body.WriteAsync(buffer, 0, bytesRead);
            _progressTracker.AddSentBytes(bytesRead);
            await context.Response.Body.FlushAsync();
        }
    }

    private string GetContentType(string filePath)
    {
        var ext = Path.GetExtension(filePath).ToLowerInvariant();
        return ext switch
        {
            ".txt" => "text/plain",
            ".bin" => "application/octet-stream",
            _ => "application/octet-stream"
        };
    }
}

4. GUI端监控进度(以WPF为例)

private async void MonitorDownloadProgress()
{
    while (true)
    {
        var progress = _progressTracker.Progress;
        Dispatcher.Invoke(() =>
        {
            DownloadProgressBar.Value = progress;
            StatusLabel.Text = $"下载进度:{progress:F1}%";
        });
        await Task.Delay(100);
    }
}

方案2:包装响应流监控进度

若需保留静态文件中间件原有功能,可通过包装响应流拦截字节写入操作,统计传输进度。

1. 进度跟踪流实现

public class ProgressTrackingStream : Stream
{
    private readonly Stream _innerStream;
    private readonly DownloadProgressTracker _progressTracker;

    public ProgressTrackingStream(Stream innerStream, DownloadProgressTracker progressTracker)
    {
        _innerStream = innerStream;
        _progressTracker = progressTracker;
    }

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

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

    public override void Write(byte[] buffer, int offset, int count)
    {
        _innerStream.Write(buffer, offset, count);
        _progressTracker.AddSentBytes(count);
    }

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

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

2. 集成到现有服务启动代码

private void RunServer(string configPath)
{
    _progressTracker = new DownloadProgressTracker();
    var builder = WebApplication.CreateBuilder();
    builder.Services.AddSingleton(_progressTracker);
    var app = builder.Build();

    // 在静态文件中间件前添加流包装逻辑
    app.Use(async (context, next) =>
    {
        var originalStream = context.Response.Body;
        try
        {
            var filePath = Path.Combine(configPath, context.Request.Path.Value.TrimStart('/'));
            if (File.Exists(filePath))
            {
                var fileInfo = new FileInfo(filePath);
                _progressTracker.Reset();
                _progressTracker.TotalBytes = fileInfo.Length;
                context.Response.Body = new ProgressTrackingStream(originalStream, _progressTracker);
            }
            await next();
        }
        finally
        {
            context.Response.Body = originalStream;
        }
    });

    app.UseStaticFiles(new StaticFileOptions()
    {
        FileProvider = new PhysicalFileProvider(configPath),
        ServeUnknownFileTypes = true,
        DefaultContentType = "text/plain"
    });

    app.Run();
}

注意事项

  • 多设备同时下载时,需扩展进度跟踪类,支持按文件路径或请求ID区分任务
  • 确保进度跟踪对象的线程安全性,避免读写冲突
  • GUI更新需遵循对应框架的跨线程操作规范(如WinForms需用Invoke)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 23:44:52