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

能否通过Blazor Server应用实现浏览器文件直接上传至Azure Blob Storage?

Blazor Server 实现直接上传文件到Azure Blob Storage(带进度跟踪)

问题背景

我在Azure App Service上托管了Blazor Server应用,当前文件上传流程是先把用户本地文件传到Web应用服务器,再从服务器传到Azure Blob Storage,这种两步上传效率极低,文件要走两次网络。我希望实现用户浏览器直接上传到Blob Storage,同时借助Azure.Storage.Blobs的进度回调更新前端进度条。试过微软官方示例大多是两步上传,JS方案也没在Blazor里跑通,现有简化代码如下:

public static async Task UploadFileToAzureBlobStorage(IBrowserFile fileToUpload, Progress<long> progressHandler, Progress<long> copyProgressHandler)
{
    // 创建带令牌的Blob URI
    Uri blobUri = new Uri($"https://storageAccountName.blob.core.windows.net/containerName/filename.txt?token");

    // 从URI创建BlobClient
    BlobClient blobClient = new BlobClient(blobUri);

    // 拼接服务器本地文件路径
    string filename = Path.Combine("folderName", "filename.txt");

    // 上传操作
    FileStream stream = new FileStream(filename, FileMode.Create);
    try
    {
        // 第一步:用户文件传到Web服务器
        await fileToUpload.OpenReadStream(fileToUpload.Size).CopyToAsync(stream, copyProgressHandler);
        
        // 第二步:Web服务器文件传到Blob Storage
        stream.Position = 0;
        await Task.Run(() => blobClient.Upload(stream, progressHandler: progressHandler));
    }
    catch (Exception ex)
    {
        // 错误处理
    }

    // 释放流
    await stream.DisposeAsync();
}

解决方案:直接上传+进度跟踪

核心思路是通过JS互操作调用Azure Storage JS SDK,让用户浏览器直接和Blob Storage交互,同时把上传进度回调到Blazor组件更新UI。

步骤1:后端生成带写入权限的SAS URI

保持你现有生成SAS URI的逻辑,确保令牌包含Write权限,且过期时间足够覆盖上传时长。示例代码:

public string GenerateBlobSasUri(string containerName, string blobName)
{
    var blobServiceClient = new BlobServiceClient("你的存储账户连接字符串");
    var containerClient = blobServiceClient.GetBlobContainerClient(containerName);
    var blobClient = containerClient.GetBlobClient(blobName);

    var sasBuilder = new BlobSasBuilder()
    {
        BlobContainerName = containerName,
        BlobName = blobName,
        ExpiresOn = DateTimeOffset.UtcNow.AddMinutes(30),
        Protocol = SasProtocol.Https
    };
    sasBuilder.SetPermissions(BlobSasPermissions.Write);

    return blobClient.GenerateSasUri(sasBuilder).ToString();
}

步骤2:Blazor组件实现文件选择与进度跟踪

创建上传组件,通过IJSRuntime调用JS上传逻辑,并接收进度回调:

@page "/direct-upload"
@inject IJSRuntime JSRuntime

<h3>直接上传到Blob Storage</h3>

<input type="file" @onchange="OnFileSelected" />
<div class="progress-bar" style="width:@progressPercent%">@progressPercent%</div>

@code {
    private IBrowserFile? _selectedFile;
    private int progressPercent;

    private async Task OnFileSelected(InputFileChangeEventArgs e)
    {
        _selectedFile = e.File;
        progressPercent = 0;

        // 获取后端生成的SAS URI
        var sasUri = await GenerateSasUriAsync("你的容器名", _selectedFile.Name);

        // 调用JS上传方法,传递文件、SAS URI和进度回调引用
        await JSRuntime.InvokeVoidAsync(
            "blobUploader.upload",
            _selectedFile,
            sasUri,
            DotNetObjectReference.Create(this));
    }

    [JSInvokable]
    public void UpdateUploadProgress(long bytesSent, long totalBytes)
    {
        progressPercent = (int)((double)bytesSent / totalBytes * 100);
        StateHasChanged();
    }

    // 实际项目中替换为调用后端接口获取SAS URI
    private Task<string> GenerateSasUriAsync(string container, string blobName)
    {
        return Task.FromResult("生成的SAS URI");
    }
}

步骤3:编写JS上传逻辑

在wwwroot/js下创建blob-upload.js,封装Azure Storage SDK的上传逻辑:

window.blobUploader = {
    async upload(file, sasUri, dotNetRef) {
        // 从SAS URI初始化Blob服务客户端
        const blobServiceClient = Azure.Storage.Blobs.BlobServiceClient.fromConnectionString(sasUri);
        const containerName = new URL(sasUri).pathname.split('/')[1];
        const containerClient = blobServiceClient.getContainerClient(containerName);
        const blobClient = containerClient.getBlobClient(file.name);

        try {
            // 上传文件并跟踪进度
            await blobClient.uploadBrowserData(file, {
                progress: (event) => {
                    const bytesSent = event.loaded;
                    const totalBytes = event.total;
                    dotNetRef.invokeMethodAsync('UpdateUploadProgress', bytesSent, totalBytes);
                }
            });
            alert('上传完成');
        } catch (error) {
            console.error('上传失败:', error);
            alert('上传失败,请重试');
        } finally {
            dotNetRef.dispose();
        }
    }
};

步骤4:引入依赖脚本

在wwwroot/index.html的body末尾添加Azure Storage JS SDK和自定义脚本:

<script src="https://aka.ms/azure-storage-blob-js"></script>
<script src="js/blob-upload.js"></script>

关键说明

  • 完全绕过App Service:文件从用户浏览器直接传到Blob Storage,避免了两次上传的带宽浪费和延迟
  • 进度跟踪:通过JS的progress回调,将实时进度传递给Blazor组件,更新UI
  • 权限安全:SAS URI仅授予临时写入权限,过期后自动失效,避免存储账户密钥泄露
  • 内存管理:使用DotNetObjectReference后要及时释放,防止Blazor Server内存泄漏

内容的提问来源于stack exchange,提问作者Eric

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 15:32:20