如何用Blazor上传大文件至WebAPI并存储到Azure Blob Storage(SDK v12)
Blazor大文件上传至Azure Blob Storage(v12 SDK)解决方案
针对4GB大文件上传需求,采用**Azure Blob分块上传(Block Blob)**机制,实现前端分片、服务端直接转发流到Azure、无服务端内存占用的完整流程。
核心思路
利用Azure Block Blob的块上传特性:
- Blazor前端将大文件拆分为固定大小分片(如100MB)
- 逐个将分片和唯一块ID发送至后端API
- 后端API直接将请求体流上传至Azure Blob的对应块,不缓存分片数据
- 所有分片上传完成后,触发Azure端的块合并操作,生成单个完整Blob
1. Blazor前端代码
<input type="file" @onchange="HandleFileSelected" /> <button @onclick="StartUpload" disabled="@isUploading">开始上传</button> <div>上传进度:@uploadProgress%</div> @code { private IReadOnlyList<IBrowserFile> selectedFiles; private bool isUploading; private int uploadProgress; private const int BlockSize = 100 * 1024 * 1024; // 100MB分片(Azure单块最大支持100MB) private List<string> uploadedBlockIds = new(); private void HandleFileSelected(InputFileChangeEventArgs e) { selectedFiles = e.GetMultipleFiles(1); } private async Task StartUpload() { if (selectedFiles?.FirstOrDefault() == null) return; isUploading = true; var targetFile = selectedFiles.First(); var uniqueBlobName = $"{Guid.NewGuid()}{Path.GetExtension(targetFile.Name)}"; long totalBytes = targetFile.Size; long uploadedBytes = 0; // 打开文件流,设置4GB最大限制 using var fileStream = targetFile.OpenReadStream(maxAllowedSize: 4L * 1024 * 1024 * 1024); int blockIndex = 0; while (uploadedBytes < totalBytes) { int bytesToRead = (int)Math.Min(BlockSize, totalBytes - uploadedBytes); var chunkBuffer = new byte[bytesToRead]; int actualReadBytes = await fileStream.ReadAsync(chunkBuffer, 0, bytesToRead); if (actualReadBytes == 0) break; // 生成Base64编码的块ID(Azure要求格式) var blockId = Convert.ToBase64String(BitConverter.GetBytes(blockIndex)); uploadedBlockIds.Add(blockId); // 发送分片至API using var chunkContent = new ByteArrayContent(chunkBuffer, 0, actualReadBytes); var response = await Http.PostAsync( $"api/upload/chunk?blobName={uniqueBlobName}&blockId={blockId}", chunkContent ); response.EnsureSuccessStatusCode(); uploadedBytes += actualReadBytes; uploadProgress = (int)((uploadedBytes * 100) / totalBytes); blockIndex++; StateHasChanged(); } // 触发块合并 var mergeResponse = await Http.PostAsync( $"api/upload/complete?blobName={uniqueBlobName}&blockIds={string.Join(",", uploadedBlockIds)}", null ); mergeResponse.EnsureSuccessStatusCode(); isUploading = false; uploadProgress = 100; StateHasChanged(); } }
2. 后端API代码
using Azure.Storage.Blobs; using Azure.Storage.Blobs.Models; using Microsoft.AspNetCore.Mvc; [ApiController] [Route("api/upload")] public class UploadController : ControllerBase { private readonly BlobServiceClient _blobServiceClient; private const string TargetContainer = "your-container-name"; // 替换为你的容器名 public UploadController(BlobServiceClient blobServiceClient) { _blobServiceClient = blobServiceClient; } /// <summary> /// 上传单个分片至Azure Blob /// </summary> [HttpPost("chunk")] public async Task<IActionResult> UploadChunk([FromQuery] string blobName, [FromQuery] string blockId) { // 获取容器客户端(不存在则创建) var containerClient = _blobServiceClient.GetBlobContainerClient(TargetContainer); await containerClient.CreateIfNotExistsAsync(PublicAccessType.Blob); // 获取Blob客户端,直接转发请求体流到Azure var blobClient = containerClient.GetBlobClient(blobName); await blobClient.StageBlockAsync(blockId, Request.Body); return Ok(); } /// <summary> /// 合并所有分片为完整Blob /// </summary> [HttpPost("complete")] public async Task<IActionResult> CompleteUpload([FromQuery] string blobName, [FromQuery] string blockIds) { var containerClient = _blobServiceClient.GetBlobContainerClient(TargetContainer); var blobClient = containerClient.GetBlobClient(blobName); // 拆分块ID列表,触发Azure端合并 var blockIdList = blockIds.Split(',').ToList(); await blobClient.CommitBlockListAsync(blockIdList); return Ok(new { BlobUrl = blobClient.Uri.AbsoluteUri }); } }
3. 服务配置
注册Azure Blob客户端(Program.cs)
// 从配置文件读取Azure Blob连接字符串 builder.Services.AddSingleton(sp => new BlobServiceClient(builder.Configuration.GetConnectionString("AzureBlobStorage")));
配置Kestrel请求大小限制(Program.cs)
builder.Services.Configure<KestrelServerOptions>(options => { // 限制单个请求大小与分片大小一致 options.Limits.MaxRequestBodySize = 100 * 1024 * 1024; });
关键注意事项
- 分片大小选择:推荐100MB,Azure标准Block Blob单块最大支持100MB,高级Block Blob可支持4GB单块
- 块ID规范:必须为Base64编码,且所有块ID长度需一致(示例中用4字节索引转换,长度固定)
- 断点续传扩展:可在上传前调用
blobClient.GetBlockListAsync获取已上传块ID,跳过重复分片 - 超时设置:需为API请求和Azure SDK操作配置合理超时时间
内容的提问来源于stack exchange,提问作者Hamlet Poghosyan
相关产品推荐
相关产品推荐

