如何通过Azure APIM以multipart/form-data格式上传大文件至Azure Blob存储?
解决Azure APIM解析multipart/form-data上传大文件到Blob Storage的问题
由于Azure APIM原生不支持context.Request.File来处理multipart/form-data格式的文件上传,我们可以通过手动解析请求体的方式提取文件内容,再转发到Azure Blob Storage。以下是完整的策略代码:
<policies> <inbound> <base /> <!-- 仅在请求为multipart/form-data时执行解析逻辑 --> <choose> <when condition="@(context.Request.Headers.GetValueOrDefault("Content-Type", "").StartsWith("multipart/form-data"))"> <!-- 1. 获取multipart边界 --> <set-variable name="boundary" value="@(context.Request.Headers.GetValueOrDefault("Content-Type", "").Split(';').FirstOrDefault(s => s.Trim().StartsWith("boundary="))?.Split('=')[1].Trim())" /> <!-- 2. 读取请求体并解析出文件内容 --> <set-body>@{ var body = context.Request.Body.As<string>(true); if (string.IsNullOrEmpty(body) || string.IsNullOrEmpty((string)context.Variables["boundary"])) { return body; } var boundary = "--" + (string)context.Variables["boundary"]; var parts = body.Split(new[] { boundary }, StringSplitOptions.RemoveEmptyEntries); // 找到包含文件的部分(假设只有一个文件字段) var filePart = parts.FirstOrDefault(p => p.Contains("filename=")); if (filePart == null) { return body; } // 提取文件内容:跳过头部,直到空行后的内容 var contentStart = filePart.IndexOf("\r\n\r\n") + 4; var contentEnd = filePart.LastIndexOf("\r\n--"); if (contentStart < 0 || contentEnd < 0) { return body; } var fileContent = filePart.Substring(contentStart, contentEnd - contentStart); // 从文件部分提取文件名(可选,也可以继续用Query参数) var fileNameMatch = System.Text.RegularExpressions.Regex.Match(filePart, @"filename=""(.*?)"""); if (fileNameMatch.Success) { context.Variables["fileName"] = fileNameMatch.Groups[1].Value; } return fileContent; }</set-body> <!-- 3. 修改请求头,适配Blob Storage的PUT要求 --> <set-header name="Content-Type" exists-action="override"> <value>application/octet-stream</value> </set-header> <!-- 4. 如果从multipart中提取了文件名,重写URI --> <rewrite-uri template="@( context.Request.Url.Query.GetValueOrDefault("blobContainer", "test") + "/" + (context.Variables.ContainsKey("fileName") ? (string)context.Variables["fileName"] : context.Request.Url.Query.GetValueOrDefault("fileNameWithExtension", "test.mp4")) )" /> </when> <!-- 非multipart请求沿用原逻辑 --> <otherwise> <rewrite-uri template="@( context.Request.Url.Query.GetValueOrDefault("blobContainer", "test") + "/" + context.Request.Url.Query.GetValueOrDefault("fileNameWithExtension", "test.mp4") )" /> </otherwise> </choose> <!-- 通用Blob Storage配置 --> <set-backend-service base-url="@{ return $"https://{{storage account}}.blob.core.windows.net/";}" /> <set-method>PUT</set-method> <set-header name="Host" exists-action="override"> <value>sacuksnprdiidlrawast6001.blob.core.windows.net</value> </set-header> <set-header name="X-Ms-Blob-Type" exists-action="override"> <value>BlockBlob</value> </set-header> <set-header name="X-Ms-Version" exists-action="override"> <value>2019-12-12</value> </set-header> <set-header name="Accept" exists-action="override"> <value>application/json</value> </set-header> <authentication-managed-identity resource="https://storage.azure.com/" /> </inbound> <backend> <base /> </backend> <outbound> <base /> </outbound> <on-error> <base /> </on-error> </policies>
关键逻辑说明
- 边界解析:从
Content-Type头中提取multipart的边界字符串,用于拆分请求体。 - 请求体解析:将请求体按边界拆分,找到包含
filename=的部分(即文件字段),跳过字段头部后提取纯文件内容。 - 头部调整:将
Content-Type改为application/octet-stream,适配Blob Storage的PUT上传要求。 - 文件名处理:可选从multipart字段中提取文件名,替代原Query参数的文件名(如果需要)。
注意事项
- 该策略假设请求中仅包含一个文件字段,如果需要支持多文件上传,需调整拆分和提取逻辑。
- 对于2GB级别的超大文件,需确保APIM的请求体大小限制已调整,避免因默认限制导致上传失败。
- 解析过程会读取整个请求体到内存,若文件过大可能引发内存压力,建议结合分块上传的方式优化(通过Blob Storage的块Blob分块API实现)。
内容的提问来源于stack exchange,提问作者Vivek Chandel
相关产品推荐
相关产品推荐

