Blazor Server大文件分片上传至CDN时文件损坏问题求助
Blazor Server分片上传文件内容损坏问题修复方案
核心问题定位
原代码的关键错误在于未正确处理ReadAsync的返回值:Stream.ReadAsync并不保证会读取到请求的全部字节数,返回值是实际读取的字节数。原代码直接使用整个预分配的chunk数组,导致当实际读取字节数小于chunkSize时,数组末尾的无效字节(默认是0)会被一并上传,最终合并后的文件包含冗余数据,导致内容损坏。
修复步骤及优化点
1. 正确处理流读取的实际字节数
修改分片读取逻辑,只使用实际读取到的字节部分:
var chunk = new byte[chunkSize]; var bytesRead = await inStream.ReadAsync(chunk, 0, (int)chunkSize); if (bytesRead == 0) { _uploading = false; break; } // 仅使用实际读取的字节创建内存流 using var chunkStream = new MemoryStream(chunk, 0, bytesRead); var fileContent = new StreamContent(chunkStream);
2. 增加上传响应校验
每次分片上传后校验响应状态,避免失败的分片被忽略:
var response = await Http.SendAsync(request); response.EnsureSuccessStatusCode(); // 上传失败时抛出异常
3. 优化Blazor Server流读取配置
Blazor Server中InputFile.OpenReadStream的参数建议设置为文件实际大小,而非long.MaxValue,减少不必要的内存开销:
using (var inStream = file.OpenReadStream(file.Size))
4. 完善Content-Type设置
为分片内容设置正确的ContentType,避免CDN处理异常:
fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType);
5. 确保资源正确释放
使用using语句管理StreamContent和MemoryStream,避免资源泄漏:
using var chunkStream = new MemoryStream(chunk, 0, bytesRead); using var fileContent = new StreamContent(chunkStream);
修改后的完整代码示例
private async Task OnInputFileChange(InputFileChangeEventArgs e) { const long CHUNKSIZE = 1024 * 400; // 可根据实际场景调整 var file = e.File; long uploadedBytes = 0; long totalBytes = file.Size; long percent = 0; int fragment = 0; long chunkSize; using (var inStream = file.OpenReadStream(file.Size)) { blockView = false; model.Url = string.Empty; _uploading = true; var contentType = Path.GetExtension(file.Name); while (_uploading) { chunkSize = uploadedBytes + CHUNKSIZE > totalBytes ? totalBytes - uploadedBytes : CHUNKSIZE; var chunk = new byte[chunkSize]; var bytesRead = await inStream.ReadAsync(chunk, 0, (int)chunkSize); if (bytesRead == 0) { _uploading = false; break; } using var chunkStream = new MemoryStream(chunk, 0, bytesRead); using var fileContent = new StreamContent(chunkStream); fileContent.Headers.ContentType = new MediaTypeHeaderValue(file.ContentType); var request = new HttpRequestMessage(HttpMethod.Put, $"https://{cdn_url}/{model.Files.Name}/{(fragment++).ToString("D8")}"); request.Headers.Add("X-Auth-Token", model.Files.Token); request.Content = fileContent; var response = await Http.SendAsync(request); response.EnsureSuccessStatusCode(); // 更新进度与UI uploadedBytes += bytesRead; percent = uploadedBytes * 100 / totalBytes; echo = $"上传 {percent}% {(uploadedBytes / 1e+6).ToString("F2")} MB / {(totalBytes / 1e+6).ToString("F2")} MB"; _uploading = percent < 100; Console.WriteLine($"分片 {fragment} 上传字节数: {bytesRead}"); await InvokeAsync(StateHasChanged); } // 发起分片合并请求 var requestItog = new HttpRequestMessage(HttpMethod.Put, $"https://{cdn_url}/{model.Files.Name + contentType}"); requestItog.Headers.Add("X-Auth-Token", model.Files.Token); requestItog.Headers.Add("X-Object-Manifest", $"{Container_Name}/{model.Files.Name}/"); var responseItog = await Http.SendAsync(requestItog); responseItog.EnsureSuccessStatusCode(); model.Url = model.Files.Name + contentType; await AddName.InvokeAsync(model.Files.Name + contentType); } }
额外检查点
- 确认CDN的
X-Object-Manifest合并规则:确保CDN会按分片文件名的字典序(00000000、00000001...)正确合并所有分片。 - Blazor Server Hub配置:已设置
MaximumReceiveMessageSize为100MB,满足大文件分片传输的SignalR限制,无需调整。
内容的提问来源于stack exchange,提问作者blakcat
相关产品推荐
相关产品推荐

