如何在Blazor WASM中实现不预先加载至内存的大文件上传?
解决方案:Blazor WASM流式上传无需服务器分片
你排查得很准确——Blazor WASM的HttpClient.PostAsync在处理StreamContent时,确实会通过ReadAsByteArrayAsync把整个流一次性读取到内存中,这对大文件或大内容上传的性能和内存占用影响很大,而问题根源正是你提到的那段.NET运行时代码。
不用在服务器端引入分片技术的话,直接使用浏览器原生的XMLHttpRequest(XHR)来实现流式上传是最直接的方案,它支持直接发送ReadableStream,不需要预先把整个内容加载到内存里。
实现步骤
创建Blazor端的C#调用方法
在你的服务或组件中注入IJSRuntime,然后封装一个调用JS的方法:using Microsoft.JSInterop; using System.IO; using System.Threading; using System.Threading.Tasks; public class UploadService { private readonly IJSRuntime _jsRuntime; public UploadService(IJSRuntime jsRuntime) { _jsRuntime = jsRuntime; } public async Task<bool> StreamUploadAsync(string uploadUrl, Stream contentStream, string contentType, CancellationToken cancellationToken = default) { try { return await _jsRuntime.InvokeAsync<bool>("streamUpload", uploadUrl, contentStream, contentType, cancellationToken); } catch (JSException ex) { // 处理JS端抛出的错误 throw new InvalidOperationException("Upload failed", ex); } } }添加对应的JavaScript代码
在你的wwwroot/index.html或者单独的JS文件中添加以下代码:window.streamUpload = async function (url, contentStream, contentType, cancellationToken) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open('POST', url); xhr.setRequestHeader('Content-Type', contentType); // 处理上传取消逻辑 const abortHandler = () => { xhr.abort(); reject(new Error('Upload was cancelled')); }; cancellationToken.addEventListener('abort', abortHandler); // 处理上传完成 xhr.onload = () => { cancellationToken.removeEventListener('abort', abortHandler); if (xhr.status >= 200 && xhr.status < 300) { resolve(true); } else { reject(new Error(`Upload failed with status code: ${xhr.status}`)); } }; // 处理网络错误 xhr.onerror = () => { cancellationToken.removeEventListener('abort', abortHandler); reject(new Error('Network error occurred during upload')); }; // 直接发送流,无需预加载到内存 xhr.send(contentStream); }); };
关键优势
- 真正的流式上传:利用浏览器原生API直接发送流,不会把整个内容加载到内存,完美解决内存占用问题。
- 无需服务器改动:服务器只需要处理常规的POST请求体即可,不需要额外的分片接收逻辑。
- 支持取消和错误处理:集成了CancellationToken来处理上传取消,同时覆盖了网络错误和HTTP状态码错误场景。
可选扩展
如果需要跟踪上传进度,可以在JS代码中添加xhr.upload.onprogress事件监听:
xhr.upload.onprogress = (event) => { if (event.lengthComputable) { const progress = (event.loaded / event.total) * 100; console.log(`Upload progress: ${progress.toFixed(2)}%`); // 这里可以通过JS互操作把进度传回Blazor端 } };
内容的提问来源于stack exchange,提问作者Peter Morris
相关产品推荐
相关产品推荐

