如何通过Azure APIM将带文件名的Multipart表单数据附件存入Azure存储账户
解决APIM处理Multipart/form-data元数据提取与Blob存储/返回问题
一、POST上传时提取元数据并存储到Blob
在APIM的入站(inbound)策略中完成以下操作:
- 提取请求的boundary
从请求头的Content-Type中解析出boundary值,用于后续解析multipart内容:
<set-variable name="boundary" value="@{ var contentType = context.Request.Headers.GetValueOrDefault("Content-Type", ""); var boundaryParts = contentType.Split(';').Where(s => s.Trim().StartsWith("boundary=")).ToList(); return boundaryParts.Any() ? boundaryParts[0].Trim().Substring(9) : string.Empty; }" />
- 解析multipart/form-data,提取name和filename
读取请求体内容,通过正则匹配content-disposition字段中的name和filename:
<set-variable name="formDataMetadata" value="@{ var body = context.Request.Body.As<string>(true); var boundary = context.Variables.GetValueOrDefault<string>("boundary"); var partRegex = new System.Text.RegularExpressions.Regex($"--{System.Text.RegularExpressions.Regex.Escape(boundary)}\\r\\ncontent-disposition: form-data; name=\"(?<name>[^\"]+)\"; filename=\"(?<filename>[^\"]+)\""); var match = partRegex.Match(body); return new { Name = match.Groups["name"].Value, Filename = match.Groups["filename"].Value, Extension = System.IO.Path.GetExtension(match.Groups["filename"].Value).TrimStart('.').ToLower(), CreatedAt = DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.ff"), UpdatedAt = DateTimeOffset.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.ff") }; }" />
- 将元数据附加到Blob存储请求
调用Azure Storage的Put Blob API时,添加自定义元数据头,同时转发请求体到存储账户:
<set-header name="x-ms-meta-name" exists-action="override"> <value>@((context.Variables.GetValueOrDefault<dynamic>("formDataMetadata")).Name)</value> </set-header> <set-header name="x-ms-meta-filename" exists-action="override"> <value>@((context.Variables.GetValueOrDefault<dynamic>("formDataMetadata")).Filename)</value> </set-header> <set-header name="x-ms-meta-extension" exists-action="override"> <value>@((context.Variables.GetValueOrDefault<dynamic>("formDataMetadata")).Extension)</value> </set-header> <set-header name="x-ms-meta-createdAt" exists-action="override"> <value>@((context.Variables.GetValueOrDefault<dynamic>("formDataMetadata")).CreatedAt)</value> </set-header> <set-header name="x-ms-meta-updatedAt" exists-action="override"> <value>@((context.Variables.GetValueOrDefault<dynamic>("formDataMetadata")).UpdatedAt)</value> </set-header> <rewrite-uri template="/{your-container-name}/{your-blob-name}" />
二、GET检索时构造指定格式的响应
在APIM的出站(outbound)策略中处理返回结果:
- 读取Blob元数据和内容
从Storage的响应头中提取自定义元数据,同时将Blob内容转换为Base64:
<set-variable name="blobMetadata" value="@{ return new { AttachmentName = context.Response.Headers.GetValueOrDefault("x-ms-meta-filename", ""), AttachmentType = context.Response.Headers.GetValueOrDefault("x-ms-meta-extension", ""), CreatedAt = context.Response.Headers.GetValueOrDefault("x-ms-meta-createdAt", ""), UpdatedAt = context.Response.Headers.GetValueOrDefault("x-ms-meta-updatedAt", "") }; }" /> <set-variable name="blobBase64" value="@{ var content = context.Response.Body.As<byte[]>(true); return Convert.ToBase64String(content); }" />
- 构造JSON响应
替换原始响应为要求的JSON格式:
<set-body>@{ var metadata = context.Variables.GetValueOrDefault<dynamic>("blobMetadata"); var base64 = context.Variables.GetValueOrDefault<string>("blobBase64"); return Newtonsoft.Json.JsonConvert.SerializeObject(new { attachmentName = metadata.AttachmentName, attachmentType = metadata.AttachmentType, attachmentData = base64, createdAt = metadata.CreatedAt, updatedAt = metadata.UpdatedAt }); }</set-body> <set-header name="Content-Type" exists-action="override"> <value>application/json</value> </set-header>
注意事项
- 确保APIM策略中使用
As<string>(true)或As<byte[]>(true)读取请求/响应体时,保留原始内容供后续处理。 - 如果存在多个form-data部分,需调整正则表达式定位目标文件对应的部分。
- Blob名称可结合业务规则生成(如filename+时间戳),避免重复覆盖。
内容的提问来源于stack exchange,提问作者etrnllrnr
相关产品推荐
相关产品推荐

