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

.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问题解决建议
  1. 确认CORS策略已正确启用
    仅注册策略还不够,需在Startup.cs的Configure方法中添加中间件启用,注意顺序要在UseRouting之后、UseAuthorization之前:

    app.UseCors("CorsPolicy");
    
  2. 检查Origin配置格式

    • 确保MyCors中的地址无多余空格(如http://localhost,http://localhost:4200不要写成http://localhost, http://localhost:4200)
    • 核对Angular应用实际运行地址,若使用HTTPS启动,需将配置改为https://localhost:4200
  3. 处理预检请求与响应头

    • 若请求包含自定义头或复杂操作(如带Credentials),需确保服务器正确响应OPTIONS请求(AllowAnyMethod已包含OPTIONS,若仍有问题可显式声明)
    • 添加暴露响应头配置,允许客户端获取Content-Disposition等必要头:
      builder.WithExposedHeaders("Content-Disposition");
      
  4. 优化响应流返回方式
    替换File方法为FileStreamResult,确保响应头正确设置:

    return new FileStreamResult(zipStream, "application/zip")
    {
        FileDownloadName = "ZipFile.zip"
    };
    

二、Azure存储超大文件下载优化方案

当前代码将所有文件缓冲到MemoryStream,大文件/多文件场景下会导致内存占用过高,以下是优化方案:

  1. 边下载边打包边返回,避免内存缓冲
    直接将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();
    
  2. 分块下载单个超大文件
    对超过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;
        }
    }
    
  3. 可控并行下载多文件
    针对多文件场景,限制并发数避免超出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);
    
  4. 预处理文件减轻API压力
    若频繁打包大量超大文件,可将打包逻辑移至Azure函数,提前生成Zip文件存储到Blob,API仅返回下载链接。


内容的提问来源于stack exchange,提问作者mnu-nasir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 15:27:50