借助托管身份从X++上传文件至Azure Blob的技术求助
解决Dynamics 365 F&O用托管身份上传Azure Blob文件的问题
X++代码实现文件上传(步骤4)
通过Managed Identity获取的访问令牌,可直接调用Azure Blob的REST API完成文件上传。以下是完整的X++代码示例,包含令牌获取和上传逻辑:
public static void uploadBlobWithManagedIdentity(str _tenantId, str _appId, str _appSecret, str _storageAccountName, str _containerName, str _blobName, str _filePath) { // 获取Azure资源访问令牌(步骤3的实现) str accessToken = getManagedIdentityToken(_tenantId, _appId, _appSecret, 'https://storage.azure.com/.default'); if (emptyStr(accessToken)) { error('未能获取有效访问令牌'); return; } // 构造Blob服务API地址 str blobApiUrl = strFmt('https://%1.blob.core.windows.net/%2/%3', _storageAccountName, _containerName, _blobName); HttpClient httpClient = new HttpClient(); HttpRequestMessage uploadRequest = new HttpRequestMessage(HttpMethod::Put, blobApiUrl); HttpResponseMessage uploadResponse; ByteArrayContent fileContent; System.IO.FileStream fileStream; System.Byte[] fileBytes; try { // 读取待上传文件(若为D365临时文件,可替换为从临时表/内存流读取) fileStream = new System.IO.FileStream(_filePath, System.IO.FileMode::Open, System.IO.FileAccess::Read); fileBytes = new System.Byte[fileStream.get_Length()]; fileStream.Read(fileBytes, 0, fileStream.get_Length()); fileStream.Close(); // 构造请求内容与头部 fileContent = new ByteArrayContent(fileBytes); fileContent.Headers().Add('x-ms-blob-type', 'BlockBlob'); uploadRequest.Content(fileContent); uploadRequest.Headers().Authorization(new System.Net.Http.Headers.AuthenticationHeaderValue('Bearer', accessToken)); // 发送上传请求 uploadResponse = httpClient.SendAsync(uploadRequest).Result; if (uploadResponse.IsSuccessStatusCode()) { info(strFmt('文件上传成功,状态码:%1', uploadResponse.StatusCode())); } else { str errorDetails = uploadResponse.Content.ReadAsStringAsync().Result; error(strFmt('上传失败:状态码%1,详情:%2', uploadResponse.StatusCode(), errorDetails)); } } catch(Exception::CLRError) { error(strFmt('CLR异常:%1', CLRInterop::getLastException().ToString())); } catch(Exception::Error) { error(strFmt('系统错误:%1', error())); } } // 辅助方法:获取Client Credentials模式的访问令牌 private static str getManagedIdentityToken(str _tenantId, str _appId, str _appSecret, str _scope) { str tokenEndpoint = strFmt('https://login.microsoftonline.com/%1/oauth2/v2.0/token', _tenantId); HttpClient httpClient = new HttpClient(); HttpRequestMessage tokenRequest = new HttpRequestMessage(HttpMethod::Post, tokenEndpoint); HttpResponseMessage tokenResponse; FormUrlEncodedContent formContent; System.Collections.Generic.Dictionary<str, str> formData = new System.Collections.Generic.Dictionary<str, str>(); formData.Add('grant_type', 'client_credentials'); formData.Add('client_id', _appId); formData.Add('client_secret', _appSecret); formData.Add('scope', _scope); formContent = new FormUrlEncodedContent(formData); tokenRequest.Content(formContent); tokenResponse = httpClient.SendAsync(tokenRequest).Result; if (tokenResponse.IsSuccessStatusCode()) { str responseJson = tokenResponse.Content.ReadAsStringAsync().Result; Newtonsoft.Json.Linq.JObject jsonObj = Newtonsoft.Json.Linq.JObject::Parse(responseJson); return jsonObj.get_Item('access_token').ToString(); } else { error(strFmt('令牌获取失败:%1', tokenResponse.ReasonPhrase())); return ''; } }
注意事项
- 确保D365环境的出站网络规则允许访问
login.microsoftonline.com和Azure Blob服务域名 - 大文件建议采用分块上传(调用
Put Block和Put Block List接口),避免请求超时 - IAM权限赋值后可能需要等待5-10分钟才会生效,若出现权限错误请重试
OOB方案推荐
1. 电子报告(ER)自定义目标
利用ER的内置Azure Blob目标功能,无需编写代码即可实现托管身份认证:
- 在ER配置中创建新的文件目标,选择Azure Blob存储
- 身份验证方式选择Managed Identity,关联已配置权限的托管身份
- 直接通过ER流程输出文件到Blob存储
2. D365存储集成
若用于业务文档管理(如附件、报表输出),可配置系统级存储集成:
- 在D365的文档管理模块中,添加Azure Blob作为存储位置
- 身份验证选择托管身份,分配对应Blob权限
- 系统自动处理文件上传、存储和访问逻辑,无需自定义代码
内容的提问来源于stack exchange,提问作者piku
相关产品推荐
相关产品推荐

