Blazor WebAssembly上传Azure Storage进度条无法实时更新求助
Blazor WebAssembly Azure Storage文件上传进度条不实时更新解决方案
问题根源
- UI渲染未触发:Blazor组件默认仅在生命周期事件或用户交互后自动渲染,后台线程更新变量不会自动通知UI刷新
- 共享变量冲突:服务类中的
totalBytesUploaded是类级别成员,多用户/多文件上传时会互相干扰进度计算 - 线程上下文问题:进度回调可能在非UI线程执行,直接修改UI变量会导致更新不及时
修复方案
1. 组件端:触发UI渲染更新
修改UpdateProgress方法,通过InvokeAsync确保在UI线程执行,并调用StateHasChanged通知组件刷新:
@inject ILargeFileUpload largeFileUpload <div class="row g-3"> <div class="form-group col-sm-12 mt-3"> <InputFile OnChange="OnInputFileChange" class="form-control" /> <br /> <button class="btn btn-sm btn-success" type="button" @onclick="UploadFile">Upload</button> <br /> <br /> @{ var progressCss = "progress" + (displayProgress ? "" : "d-none"); progressWidthStyle = $"{fileProgressPercent}%"; } <div class="@progressCss"> <div class="progress-bar" role="progressbar" style="width:@progressWidthStyle" aria-valuenow="@fileProgressPercent" aria-valuemin="0" aria-valuemax="100"> @fileProgressPercent% </div> <div> <br /> <label>@labelText</label> <br /> </div> </div> </div> </div> @code { private bool displayProgress; private int fileProgressPercent = 0; private string progressWidthStyle = "0%"; private IBrowserFile? file; private string labelText = ""; private void OnInputFileChange(InputFileChangeEventArgs e) { file = e.File; } private async Task UploadFile() { try { if (file is not null) { displayProgress = true; fileProgressPercent = 0; labelText = $"开始上传 {file.Name}"; await InvokeAsync(StateHasChanged); // 初始显示进度条 await largeFileUpload.UploadFile(file, UpdateProgress); labelText = $"Upload of {file.Name} file successful"; } } catch (Exception ex) { labelText = $"Upload Failed due to {ex.Message}"; } finally { await InvokeAsync(StateHasChanged); } } private async Task UpdateProgress(int progress) { // 在UI线程更新变量并触发渲染 await InvokeAsync(() => { fileProgressPercent = progress; labelText = $"正在上传 {file.Name}: {progress}%"; StateHasChanged(); }); } }
2. 服务端:修复变量作用域与进度计算
将totalBytesUploaded改为方法内部变量,避免多上传任务冲突;同时确保进度计算精度:
public interface ILargeFileUpload { Task UploadFile(IBrowserFile file, Action<int> onProgress); } [RequestSizeLimit(500 * 1024 * 1024)] public class LargeFileUpload : ILargeFileUpload { private readonly ILogger<LargeFileUpload> _logger; private readonly long maxFileSize = 500 * 1024 * 1024; public LargeFileUpload(ILogger<LargeFileUpload> logger) { _logger = logger; } public async Task UploadFile(IBrowserFile file, Action<int> onProgress) { if (file is null) return; try { var connectionString = "#########"; var containerName = "#########"; var blobName = file.Name; BlockBlobClient blockBlobClient = new BlockBlobClient(connectionString, containerName, blobName); List<string> blockList = new List<string>(); long totalBytesUploaded = 0; // 改为方法内部变量,避免多任务冲突 using var stream = file.OpenReadStream(maxFileSize); while (true) { byte[] b = new byte[4 * 1024 * 1024]; var bytesRead = await stream.ReadAsync(b, 0, b.Length); if (bytesRead == 0) break; string blockId = Guid.NewGuid().ToString(); string base64BlockId = Convert.ToBase64String(Encoding.UTF8.GetBytes(blockId)); // 使用MemoryStream时只传递实际读取的字节数,避免上传空字节 using var blockStream = new MemoryStream(b, 0, bytesRead); await blockBlobClient.StageBlockAsync(base64BlockId, blockStream); blockList.Add(base64BlockId); totalBytesUploaded += bytesRead; int progress = (int)Math.Round((totalBytesUploaded / (double)file.Size) * 100); onProgress(progress); } await blockBlobClient.CommitBlockListAsync(blockList); onProgress(100); // 确保最终进度显示100% } catch (Exception ex) { _logger.LogError(ex, "文件上传失败"); throw; // 抛出异常让组件捕获处理 } } }
关键修改说明
- InvokeAsync + StateHasChanged:强制Blazor在UI线程更新组件状态,确保进度变化实时反映到UI
- 方法内部变量:
totalBytesUploaded改为方法内声明,避免多上传任务互相覆盖进度值 - 精确进度计算:使用
Math.Round确保进度百分比显示更准确,最后手动触发100%进度 - Stream优化:读取文件流时只传递实际读取的字节数,避免上传冗余空字节
内容的提问来源于stack exchange,提问作者Morgan
相关产品推荐
相关产品推荐

