如何在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.
相关产品推荐
相关产品推荐

