Blazor Server(.NET 9)批量处理图片时动态渲染仅在结束时生效问题
问题:Blazor Server批量处理图片时无法逐张实时预览
我基于.NET 9开发了Blazor Server应用,已在Program.cs中启用Interactive Server渲染模式:
app.MapRazorComponents<App>() .AddInteractiveServerRenderMode();
文件上传进度条功能正常,但批量处理图片时出现问题:本该每处理完一张就向用户展示预览图,实际却要等所有图片处理完成后才统一刷新UI。
图片处理逻辑代码:
private async Task ProcessImages() { Bitmap processedimage = null; foreach (var file in selectedFiles) { using (var img = Bitmap.FromFile(file)) { processedimage = imageservice.Process((Bitmap)img); using (var ms = new MemoryStream()) { using (var bitmap = new Bitmap(processedimage )) { bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); ImageUri = $"data:image/png;base64,{Convert.ToBase64String(ms.ToArray())}"; StateHasChanged(); } } } } }
图片展示的HTML代码:
<img src=@ImageUri asp-append-version="true" width="190px" style="float: none; text-align: left; border-radius: 10px; border: 2px dotted #0332ff; padding: 40px" />
问题原因
- 虽然
ProcessImages标记为async,但内部没有真正的异步等待操作,本质是同步执行的长任务,会持续占用UI线程,导致Blazor无法及时推送UI更新。 StateHasChanged()仅标记组件需要重新渲染,但在同步任务阻塞期间,渲染请求会被积压,直到整个任务完成后才一次性执行。
解决方案
方案1:插入异步等待释放UI线程
在每次处理完一张图片后,通过await Task.Yield()让UI线程有机会处理渲染更新:
private async Task ProcessImages() { Bitmap processedimage = null; foreach (var file in selectedFiles) { using (var img = Bitmap.FromFile(file)) { processedimage = imageservice.Process((Bitmap)img); using (var ms = new MemoryStream()) { using (var bitmap = new Bitmap(processedimage)) { bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); ImageUri = $"data:image/png;base64,{Convert.ToBase64String(ms.ToArray())}"; StateHasChanged(); // 释放UI线程处理渲染 await Task.Yield(); } } } } }
方案2:将图片处理移到后台线程
如果图片处理耗时较长,建议把处理逻辑放到后台线程,彻底避免阻塞UI:
private async Task ProcessImages() { foreach (var file in selectedFiles) { // 后台线程执行图片处理 var processedImage = await Task.Run(() => { using (var img = Bitmap.FromFile(file)) { return imageservice.Process((Bitmap)img); } }); using (var ms = new MemoryStream()) { using (var bitmap = new Bitmap(processedImage)) { bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg); // 确保在UI线程更新状态 await InvokeAsync(() => { ImageUri = $"data:image/png;base64,{Convert.ToBase64String(ms.ToArray())}"; StateHasChanged(); }); await Task.Yield(); } } } }
额外优化建议
如果需要展示所有已处理的图片,不要用单个ImageUri覆盖,改用列表存储每张图片的Base64地址,再通过循环渲染多张图片:
// 定义列表存储所有处理后的图片Uri private List<string> ProcessedImageUris = new(); // 修改处理逻辑 await InvokeAsync(() => { ProcessedImageUris.Add($"data:image/png;base64,{Convert.ToBase64String(ms.ToArray())}"); StateHasChanged(); });
渲染部分:
@foreach (var uri in ProcessedImageUris) { <img src=@uri asp-append-version="true" width="190px" style="float: none; text-align: left; border-radius: 10px; border: 2px dotted #0332ff; padding: 40px; margin: 10px;" /> }
内容的提问来源于stack exchange,提问作者techno
相关产品推荐
相关产品推荐

