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

借助托管身份从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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.16 10:33:17