You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor WebAssembly上传Azure Storage进度条无法实时更新求助

Blazor WebAssembly Azure Storage文件上传进度条不实时更新解决方案

问题根源

  1. UI渲染未触发:Blazor组件默认仅在生命周期事件或用户交互后自动渲染,后台线程更新变量不会自动通知UI刷新
  2. 共享变量冲突:服务类中的totalBytesUploaded是类级别成员,多用户/多文件上传时会互相干扰进度计算
  3. 线程上下文问题:进度回调可能在非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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:06:35