Azure App Service上传速度过慢问题排查与优化咨询
问题原因分析
- Base64编码的体积膨胀:Base64会让原始二进制文件体积增加约33%,40-80MB的文件会变成53-107MB,大幅增加了网络传输量和服务端的解析耗时。
- 全量内存加载的性能瓶颈:App Service B接收请求时,会把整个解码后的字节数组加载到内存中,S1层仅1.75GB的内存容量,大文件容易引发内存压力,导致GC频繁触发,拖慢处理速度。
- JSON序列化/反序列化的额外开销:将大字节数组序列化为JSON再传输,服务端反序列化时需要耗费大量CPU和内存资源,这是大文件场景下的低效传输方式。
- Blob上传方式不合理:使用MemoryStream一次性上传全量文件,没有采用分块上传机制,大文件单次上传易受网络波动影响,且无法利用Blob存储的分块优化能力。
- S1层资源限制:S1层的CPU、网络带宽都有上限,大文件的传输、解析和上传操作很容易触达这些限制,导致处理速度骤降。
优化方案
- 改用二进制流式传输,抛弃Base64
用multipart/form-data直接传输二进制文件,避免编码膨胀和JSON序列化开销:
App Service A端示例:
App Service B端示例:private static async Task UploadFileToAppServiceB(HttpClient client, string fileName, byte[] fileContent) { var path = "https://www.foo.bar/upload"; using var content = new MultipartFormDataContent(); using var fileStreamContent = new ByteArrayContent(fileContent); content.Add(fileStreamContent, "file", fileName); client.Timeout = TimeSpan.FromMinutes(5); var response = await client.PostAsync(path, content); response.EnsureSuccessStatusCode(); }[HttpPost("upload")] [RequestSizeLimit(209715200)] public async Task UploadFileToBlobStorage(IFormFile file) { var blobContainerClient = GetContainerClient(); var blobClient = blobContainerClient.GetBlobClient(file.FileName); await using var fileStream = file.OpenReadStream(); await blobClient.UploadAsync(fileStream, true); } - 流式处理,避免全量加载内存
服务端接收文件时直接使用请求流或IFormFile的流,不将整个文件加载到字节数组,减少内存占用和GC压力。 - 启用Blob分块上传
对于大文件,使用BlockBlobClient进行分块上传,将文件分割为多个4MB-100MB的块并行上传,提升速度和稳定性:public async Task UploadFileInBlocks(IFormFile file) { var blobContainerClient = GetContainerClient(); var blockBlobClient = blobContainerClient.GetBlockBlobClient(file.FileName); var blockSize = 4 * 1024 * 1024; // 4MB块大小 var blockIds = new List<string>(); await using var stream = file.OpenReadStream(); var buffer = new byte[blockSize]; int bytesRead; int blockIndex = 0; while ((bytesRead = await stream.ReadAsync(buffer, 0, blockSize)) > 0) { var blockId = Convert.ToBase64String(BitConverter.GetBytes(blockIndex++)); blockIds.Add(blockId); await blockBlobClient.StageBlockAsync(blockId, new MemoryStream(buffer, 0, bytesRead)); } await blockBlobClient.CommitBlockListAsync(blockIds); } - 升级App Service层
如果业务需求无法降低文件大小,考虑将S1升级到S2/S3层,获得更高的CPU、内存和网络带宽,缓解资源瓶颈。 - 跳过中转,直接上传Blob
让App Service A直接上传到Blob Storage:由App Service B生成Blob的SAS令牌,A拿到令牌后直接上传,B仅负责权限校验和业务逻辑,减少中间环节的开销。 - 优化HttpClient配置
调整HttpClient的MaxResponseContentBufferSize,或使用HttpCompletionOption.ResponseHeadersRead避免全量缓存响应,提升传输效率。
内容的提问来源于stack exchange,提问作者Andreas F
相关产品推荐
相关产品推荐

