.NET MVC文件返回端点随机失败问题排查求助
FileOptions.DeleteOnClose 我实现了一个MVC端点用于返回文件,并且在客户端下载完成后在服务器端删除该文件。我的实现参考了Stack Overflow上的方案,区别在于我的端点返回的是纯文本文件。
服务器端代码
[Authorize, HttpGet] public FileResult DownloadLogFile(string fileName) { var path = Path.Combine(this.logFilesRoot, fileName); var fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.None, 4096, FileOptions.DeleteOnClose); return File(fs, MediaTypeNames.Text.Plain, fileName); }
这段代码大部分情况下正常工作,但大于40MB的大文件会偶尔随机下载失败,Chrome报错截图如下:
我也在Firefox和Edge中测试过,同样出现问题,所以不是浏览器的问题。
环境细节
- .NET 7
- ASP.NET Core 6
- Windows操作系统
编辑更新
我已经将端点方法改为POST,但问题依旧。现在JS的fetch方法会抛出net::ERR_CONNECTION_RESET 200异常。
客户端代码
const redirectToLogFile = (fileName: SignalR.IDownloadableLogFileDetails): void => { const url = `${Utils.root}home/DownloadLogFile/${fileName.FileName}`; fetch(url, { method: "POST" }) .then(response => response.blob()) .then(blob => { const a = document.createElement("a"); a.href = window.URL.createObjectURL(blob); a.download = fileName; a.click(); }) .catch(err => console.log(err)); }
可能的失败原因分析
FileShare.None引发的文件锁定冲突
使用FileShare.None会对文件施加独占锁定,若服务器端有其他进程(如日志写入程序)尝试访问该文件,或ASP.NET Core请求处理中出现意外重试/并发访问,就会触发文件访问失败,进而中断下载。大文件下载耗时更长,这类冲突的发生概率也更高。FileOptions.DeleteOnClose的时机偏差
Windows系统中,标记DeleteOnClose的文件会在文件流关闭时被删除。如果ASP.NET Core响应管道在文件未完全发送给客户端时就提前关闭了文件流(比如请求超时、连接中断),文件会被提前删除,客户端下载到不完整内容后触发错误。大文件传输时间更长,更易出现这类情况。请求超时设置不足
ASP.NET Core默认请求超时时间可能不足以支撑大文件下载,若40MB以上文件的下载耗时超过默认超时阈值,服务器会主动断开连接,引发ERR_CONNECTION_RESET错误。内存/缓冲区限制
直接返回FileStream时,ASP.NET Core可能会尝试将文件内容缓冲到内存中。大文件会导致内存压力过高,进而引发意外的连接中断或进程异常。
解决方案建议
- 调整文件共享模式:将
FileShare.None改为FileShare.Read,允许其他进程读取文件(无写入需求时),降低锁定冲突概率。 - 改用后台删除逻辑:放弃
DeleteOnClose,改为下载完成后通过后台任务(如Hangfire、IHostedService)删除文件。可在响应完成后记录待删除文件路径,由后台服务定期清理已完成下载的文件。 - 延长请求超时时间:在
Program.cs中配置更长的请求超时,示例代码:builder.WebHost.ConfigureKestrel(options => { options.Limits.RequestHeadersTimeout = TimeSpan.FromMinutes(5); options.Limits.IdleTimeout = TimeSpan.FromMinutes(5); }); - 启用流式传输:返回
FileStreamResult并明确设置EnableRangeProcessing = true,确保ASP.NET Core采用流式传输而非将整个文件缓冲到内存,同时避免使用可能触发缓冲的中间件。 - 优化客户端下载逻辑:客户端用
fetch获取blob的方式对大文件来说内存占用过高,可改用直接跳转下载链接的方式(如设置window.location.href),让浏览器直接处理下载,降低前端内存压力。
内容的提问来源于stack exchange,提问作者LA.27

