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

如何在Kestrel Web服务器上监控/记录文件上传进度?

服务端监控大文件上传进度的解决方案

默认的IFormFile是在请求完全接收后才完成绑定,所以无法实时监控上传进度。要实现实时进度统计,需要绕过IFormFile的自动处理,手动读取请求流并在读取过程中计算进度。以下是两种可行的实现方式:

方式一:在Action中直接读取请求流(适用于非表单上传或简单表单场景)

如果你的上传请求是直接发送文件流(而非multipart/form-data表单),可以直接读取Request.Body并统计进度:

[HttpPost("file-upload")]
public async Task<IActionResult> HandleFileUpload()
{
    // 从查询参数或请求头获取uuid
    var uuid = Request.Query["uuid"].FirstOrDefault();
    if (string.IsNullOrEmpty(uuid))
    {
        return BadRequest("uuid参数不能为空");
    }

    // 获取总文件大小(需前端在请求头中传递Content-Length)
    var totalBytes = Request.ContentLength ?? 0;
    var bytesRead = 0L;
    var buffer = new byte[8192]; // 8KB缓冲区,可根据调整

    // 这里可以替换为实际的文件写入流,比如磁盘文件流
    using var targetStream = new MemoryStream();

    int read;
    while ((read = await Request.Body.ReadAsync(buffer)) > 0)
    {
        await targetStream.WriteAsync(buffer, 0, read);
        bytesRead += read;

        // 计算并记录进度
        var progress = (double)bytesRead / totalBytes * 100;
        // 可将进度存入内存缓存、Redis或数据库,供前端查询
        // 示例:_cache.Set(uuid, progress, TimeSpan.FromMinutes(30));
        Console.WriteLine($"[{uuid}] 上传进度:{progress:F2}%");
    }

    // 后续处理文件(比如保存到磁盘)
    // targetStream.Seek(0, SeekOrigin.Begin);
    // await using var fileStream = new FileStream($"uploads/{uuid}.bin", FileMode.Create);
    // await targetStream.CopyToAsync(fileStream);

    return Ok(new { Message = "上传完成", Progress = 100 });
}

方式二:解析Multipart表单(适用于带参数的表单上传)

如果你的上传是通过multipart/form-data表单提交(包含文件和其他参数),需要用MultipartReader解析表单内容,同时统计文件流的读取进度:

[HttpPost("file-upload")]
public async Task<IActionResult> HandleFileUpload()
{
    if (!Request.ContentType.StartsWith("multipart/form-data"))
    {
        return BadRequest("请求Content-Type必须为multipart/form-data");
    }

    var uuid = string.Empty;
    var boundary = MultipartRequestHelper.GetBoundary(
        MediaTypeHeaderValue.Parse(Request.ContentType), 
        100); // 100为边界最大长度限制
    var reader = new MultipartReader(boundary, Request.Body);
    var section = await reader.ReadNextSectionAsync();

    while (section != null)
    {
        if (ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition))
        {
            // 读取uuid参数
            if (contentDisposition.Name.Equals("uuid", StringComparison.OrdinalIgnoreCase))
            {
                using var streamReader = new StreamReader(section.Body);
                uuid = await streamReader.ReadToEndAsync();
            }
            // 读取文件内容并统计进度
            else if (contentDisposition.FileName.HasValue)
            {
                var totalBytes = section.Body.Length;
                var bytesRead = 0L;
                var buffer = new byte[8192];
                int read;

                // 替换为实际的文件写入流
                using var fileStream = new FileStream($"uploads/{contentDisposition.FileName}", FileMode.Create);
                while ((read = await section.Body.ReadAsync(buffer)) > 0)
                {
                    await fileStream.WriteAsync(buffer, 0, read);
                    bytesRead += read;
                    var progress = (double)bytesRead / totalBytes * 100;
                    Console.WriteLine($"[{uuid}] 上传进度:{progress:F2}%");
                }
            }
        }
        section = await reader.ReadNextSectionAsync();
    }

    return Ok(new { Message = "上传完成", Progress = 100 });
}

前端实时查询进度的补充

如果需要前端实时获取进度,可以新增一个查询接口,从缓存或数据库中读取对应uuid的进度:

[HttpGet("upload-progress/{uuid}")]
public IActionResult GetUploadProgress(string uuid)
{
    if (_cache.TryGetValue(uuid, out double progress))
    {
        return Ok(new { Progress = progress });
    }
    return NotFound("未找到对应的上传任务");
}

内容的提问来源于stack exchange,提问作者Justin C.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 13:10:49