CRM Online 8.2中如何向HTML Web Resource传递ArrayBuffer实现文件下载
针对你在CRM Online 8.2里遇到的文件下载问题,我整理了几个可行的解决方案,适配CRM的沙箱限制和参数传递规则:
方案1:将ArrayBuffer转Base64字符串传递给WebResource
既然customParameters只能传字符串,我们可以把ArrayBuffer转成Base64编码,再传递给你的Shared/Download页面,在页面内解码后生成Blob触发下载。
表单脚本端(获取ArrayBuffer后处理):
// ArrayBuffer转Base64的工具函数 function arrayBufferToBase64(buffer) { let binary = ''; const bytes = new Uint8Array(buffer); for (let i = 0; i < bytes.byteLength; i++) { binary += String.fromCharCode(bytes[i]); } return window.btoa(binary); } // 假设你已经拿到了arraybuffer、filename和文件type const base64Data = arrayBufferToBase64(arraybuffer); // 拼接参数并编码特殊字符 const customParams = `filename=${encodeURIComponent(filename)}&type=${encodeURIComponent(type)}&data=${base64Data}`; // 打开WebResource Xrm.Utility.openWebResource('Shared/Download', customParams, 300, 300);
Shared/Download.html端(解码并下载):
<!DOCTYPE html> <html> <head> <script> // 获取URL参数的工具函数 function getQueryParam(name) { return new URLSearchParams(window.location.search).get(name); } window.onload = () => { const filename = decodeURIComponent(getQueryParam('filename')); const type = decodeURIComponent(getQueryParam('type')); const base64Data = getQueryParam('data'); // Base64解码回Blob const byteChars = window.atob(base64Data); const byteNumbers = new Array(byteChars.length); for (let i = 0; i < byteChars.length; i++) { byteNumbers[i] = byteChars.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); const blob = new Blob([byteArray], { type }); // 触发下载 if (window.navigator.msSaveBlob) { window.navigator.msSaveBlob(blob, filename); } else { const downloadUrl = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = downloadUrl; a.download = filename; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(downloadUrl); } // 下载完成后关闭窗口 window.close(); }; </script> </head> <body></body> </html>
⚠️ 注意:这个方案适合小文件,因为Base64会让文件体积增加约33%,如果文件太大可能会触发URL长度限制。
方案2:直接在WebResource中通过Web API拉取文件(适合大文件)
如果文件比较大,传递Base64不太现实,我们可以换个思路:不在表单脚本里获取文件内容,而是把文件的标识(比如实体ID、字段名)传给WebResource,让WebResource直接调用CRM Web API获取文件Blob,再触发下载。
表单脚本端(传递文件标识):
const recordId = Xrm.Page.data.entity.getId().replace(/[{}]/g, ''); // 去掉ID的大括号 const customParams = `recordId=${recordId}&fieldName=your_file_field_name&filename=${encodeURIComponent('your_file_name.ext')}`; Xrm.Utility.openWebResource('Shared/Download', customParams, 300, 300);
Shared/Download.html端(调用Web API下载):
<!DOCTYPE html> <html> <head> <!-- 引入CRM全局上下文,自动处理认证 --> <script src="ClientGlobalContext.js.aspx" type="text/javascript"></script> <script> function getQueryParam(name) { return new URLSearchParams(window.location.search).get(name); } window.onload = () => { const recordId = getQueryParam('recordId'); const fieldName = getQueryParam('fieldName'); const filename = decodeURIComponent(getQueryParam('filename')); const clientUrl = Xrm.Utility.getGlobalContext().getClientUrl(); // 调用Web API获取文件Blob const req = new XMLHttpRequest(); req.open('GET', `${clientUrl}/api/data/v8.2/your_entity_name(${recordId})/${fieldName}/$value`, true); req.responseType = 'blob'; req.setRequestHeader('OData-MaxVersion', '4.0'); req.setRequestHeader('OData-Version', '4.0'); req.setRequestHeader('Accept', 'application/octet-stream'); req.onload = () => { if (req.status === 200) { const blob = req.response; // 触发下载逻辑和之前一致 if (window.navigator.msSaveBlob) { window.navigator.msSaveBlob(blob, filename); } else { const downloadUrl = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.href = downloadUrl; a.download = filename; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(downloadUrl); } } window.close(); }; req.send(); }; </script> </head> <body></body> </html>
这个方案不需要传递大体积数据,完全适配大文件场景,而且利用CRM的Web API自动处理权限,不需要额外认证逻辑。
额外提示:关于CRM沙箱的DOM操作限制
其实CRM 8.2的沙箱并非完全禁止document.createElement,只是对直接操作表单DOM有严格限制。在独立的WebResource页面里,你完全可以正常使用DOM操作来创建a标签触发下载,这也是上面两个方案可行的原因。
内容的提问来源于stack exchange,提问作者sadzag
相关产品推荐
相关产品推荐

