Blazor Server应用间歇性丢失提交文件附件问题排查求助
问题诊断与修复方案
核心问题:流读取不完整
你的代码里直接用await stream.ReadAsync(buf)读取流,但ReadAsync的返回值是实际读取的字节数,它没法保证一次性读完整个流的所有数据。本地/内网网络稳定,数据传输快,大概率一次就读完了;但外网或平板网络不稳定、延迟高时,就会出现读取不完整(部分丢失)或者完全没读到(全无丢失)的情况。
代码里的具体问题
不管是图片处理还是PDF读取部分,都存在这个漏洞:
var buf = new byte[resizedFile.Size]; using (var stream = resizedFile.OpenReadStream(maxAllowedSize:10000000)) { await stream.ReadAsync(buf); // 只读取一次,网络波动时大概率读不全 }
网络波动时,一次ReadAsync读取的字节数会小于buf的长度,剩下的字节都是默认的0,最终存到数据库的就是不完整的附件,甚至是空数据。
修复方案:循环读取流直到完成
替换原有的读取逻辑,确保把流里的所有数据都读到字节数组里。可以先封装一个通用读取方法,再修改业务代码:
通用流读取方法
private async Task<byte[]> ReadStreamToByteArrayAsync(Stream stream) { using var memoryStream = new MemoryStream(); await stream.CopyToAsync(memoryStream); return memoryStream.ToArray(); }
修改后的OnChange方法
List<PkgAttachments> SelectedFiles = new List<PkgAttachments>(); async Task OnChange(InputFileChangeEventArgs e) { var files = e.GetMultipleFiles(); foreach(var file in files) { // 替换扩展名获取方式,避免文件名无"."时报错 string fileExt = Path.GetExtension(file.Name).TrimStart('.').ToUpper(); if (new[] {"JPG", "JPEG", "BMP", "PNG"}.Contains(fileExt)) { var resizedFile = await file.RequestImageFileAsync(file.ContentType, 830, 715); using (var stream = resizedFile.OpenReadStream(maxAllowedSize:10000000)) { byte[] buf = await ReadStreamToByteArrayAsync(stream); SelectedFiles.Add(new PkgAttachments { Data = buf, ContentType = file.ContentType, Name = file.Name }); } } else if (fileExt == "PDF") { using (var stream = file.OpenReadStream(maxAllowedSize:10000000)) { byte[] buf = await ReadStreamToByteArrayAsync(stream); SelectedFiles.Add(new PkgAttachments { Data = buf, ContentType = file.ContentType, Name = file.Name }); } } } }
额外优化建议
- 扩展名获取逻辑:用
Path.GetExtension替代原有的Substring方式,避免文件名不含.时抛出异常。 - 数据完整性校验:添加到
SelectedFiles前,可检查buf.Length是否匹配文件预期大小(比如图片的resizedFile.Size),进一步确保数据完整。
其他排查方向
- 调整SignalR超时配置:外网环境下Blazor Server的SignalR连接可能因超时中断,导致附件传输中断。在
Program.cs中修改配置:builder.Services.AddServerSideBlazor() .AddHubOptions(options => { options.ClientTimeoutInterval = TimeSpan.FromMinutes(5); options.KeepAliveInterval = TimeSpan.FromMinutes(2); }); - 检查数据库字段限制:确认
varbinary(max)字段没有额外的大小限制(虽然理论上支持2GB,但存储过程或实体模型可能有自定义限制)。
内容的提问来源于stack exchange,提问作者jnelson
相关产品推荐
相关产品推荐

