.NET 6 Web API从Azure存储下载多份大文件Zip的CORS问题及优化咨询
问题背景
我在.NET 6中开发了一个API,功能是从Azure存储下载任意数量的大文件并打包成Zip格式返回。API核心代码如下:
//Get the document name lists from database List<MyDocuments> docLists = await GetMyDocumentNameAsync(countryIds); string accessKey = "*******************************"; string containerName = "TestFolderName"; using (MemoryStream zipStream = new MemoryStream()) { using (ZipArchive zipArchive = new ZipArchive(zipStream, ZipArchiveMode.Create, true)) { BlobServiceClient blobServiceClient = new BlobServiceClient(accessKey); BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName); foreach (var doc in docLists) { BlobClient blobClient = containerClient.GetBlobClient(doc.FileURL); BlobDownloadInfo downloadInfo = await blobClient.DownloadAsync(); ZipArchiveEntry entry = zipArchive.CreateEntry(doc.FileName); using (Stream entryStream = entry.Open()) { await downloadInfo.Content.CopyToAsync(entryStream); } } } // Reset the stream position to the beginning zipStream.Position = 0; return File(zipStream, "application/zip", "ZipFile.zip"); }
从Angular应用调用该API时遇到CORS错误,已在Startup.cs配置CORS策略:
services.AddCors(options => options.AddPolicy("CorsPolicy", builder => builder .WithOrigins(_appConfiguration["MyCors"].Split(",").Select(o =>o.RemovePostFix("/")).ToArray()) .AllowAnyHeader() .AllowAnyMethod() .AllowCredentials()) );
Appsettings配置:
"MyCors": "http://localhost,http://localhost:4200"
报错信息:
- 截图1:请求的资源上不存在'Access-Control-Allow-Origin'标头
- 截图2:预检请求的响应未通过访问控制检查
一、CORS问题解决建议
确认CORS策略已正确启用
仅注册策略还不够,需在Startup.cs的Configure方法中添加中间件启用,注意顺序要在UseRouting之后、UseAuthorization之前:app.UseCors("CorsPolicy");检查Origin配置格式
- 确保
MyCors中的地址无多余空格(如http://localhost,http://localhost:4200不要写成http://localhost, http://localhost:4200) - 核对Angular应用实际运行地址,若使用HTTPS启动,需将配置改为
https://localhost:4200
- 确保
处理预检请求与响应头
- 若请求包含自定义头或复杂操作(如带Credentials),需确保服务器正确响应OPTIONS请求(
AllowAnyMethod已包含OPTIONS,若仍有问题可显式声明) - 添加暴露响应头配置,允许客户端获取
Content-Disposition等必要头:builder.WithExposedHeaders("Content-Disposition");
- 若请求包含自定义头或复杂操作(如带Credentials),需确保服务器正确响应OPTIONS请求(
优化响应流返回方式
替换File方法为FileStreamResult,确保响应头正确设置:return new FileStreamResult(zipStream, "application/zip") { FileDownloadName = "ZipFile.zip" };
二、Azure存储超大文件下载优化方案
当前代码将所有文件缓冲到MemoryStream,大文件/多文件场景下会导致内存占用过高,以下是优化方案:
边下载边打包边返回,避免内存缓冲
直接将Zip内容写入API响应流,减少内存占用:var response = HttpContext.Response; response.Headers.Add("Content-Disposition", "attachment; filename=ZipFile.zip"); response.ContentType = "application/zip"; using (ZipArchive zipArchive = new ZipArchive(response.Body, ZipArchiveMode.Create, true)) { BlobServiceClient blobServiceClient = new BlobServiceClient(accessKey); BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName); foreach (var doc in docLists) { BlobClient blobClient = containerClient.GetBlobClient(doc.FileURL); using (var blobStream = await blobClient.OpenReadAsync()) { ZipArchiveEntry entry = zipArchive.CreateEntry(doc.FileName); using (Stream entryStream = entry.Open()) { await blobStream.CopyToAsync(entryStream, 81920); // 80KB缓冲区提升拷贝效率 } } } } return new EmptyResult();分块下载单个超大文件
对超过1GB的文件,采用分块下载避免一次性加载整个文件到内存:var blobProperties = await blobClient.GetPropertiesAsync(); long fileSize = blobProperties.Value.ContentLength; long offset = 0; const long chunkSize = 1024 * 1024 * 8; // 8MB分块 using (Stream entryStream = entry.Open()) { while (offset < fileSize) { long currentChunkSize = Math.Min(chunkSize, fileSize - offset); BlobDownloadInfo downloadInfo = await blobClient.DownloadAsync(new HttpRange(offset, currentChunkSize)); await downloadInfo.Content.CopyToAsync(entryStream); offset += currentChunkSize; } }可控并行下载多文件
针对多文件场景,限制并发数避免超出Azure存储请求限制:var semaphore = new SemaphoreSlim(5); // 限制并发数为5 var tasks = docLists.Select(async doc => { await semaphore.WaitAsync(); try { BlobClient blobClient = containerClient.GetBlobClient(doc.FileURL); using (var blobStream = await blobClient.OpenReadAsync()) { ZipArchiveEntry entry = zipArchive.CreateEntry(doc.FileName); using (Stream entryStream = entry.Open()) { await blobStream.CopyToAsync(entryStream, 81920); } } } finally { semaphore.Release(); } }); await Task.WhenAll(tasks);预处理文件减轻API压力
若频繁打包大量超大文件,可将打包逻辑移至Azure函数,提前生成Zip文件存储到Blob,API仅返回下载链接。
内容的提问来源于stack exchange,提问作者mnu-nasir
相关产品推荐
相关产品推荐

