如何通过AstroZero脚本,导出xls文件让用户下载?
问题描述
AstroZero提供了“excel”脚本标准库,用于操作Excel文件,例如生成Excel文件。由于AstroZero脚本编排的标准输出格式是JSON,因此这里需要做一些特殊处理,让系统生成非JSON数据才能实现xlsx文件导出。
操作步骤
- 参考开发一个简单脚本实例中操作,创建一个名称为“cube__download”的空白脚本。
- 在脚本编辑器中,输入如下脚本代码。
import * as excel from 'excel'; import * as context from 'context'; import * as buffer from 'buffer'; export class Downlaod { @action.method({ input: "Input", output: "Output", description: "do a operation" }) run(): void { // 原始数据,可以从数据表中获取,此处为了演示方便,使用静态数据 let binary = excel.encode(['a', 'b', 'c'], [{ 'a': 1, 'b': 2, 'c': 3 }, { 'a': 4, 'b': 5, 'c': 6 }, { 'a': 7, 'b': 2, 'c': 3 }]); // 对于 Excel2007 以上版本的 .xlsx 文件,需要设置内容类型为 application/vnd.openxmlformats-officedocument.spreadsheetml.sheet context.getHttp().response.setHeader('Content-Type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;base64'); // 直接把二进制作为请求体返回 context.getHttp().response.setBody(buffer.fromBytes(binary).toString(buffer.Encoding.Base64)); } }
此时,后端返回了一个base64编码的二进制数据,并非JSON格式,所以直接运行脚本不会有输出,需要在前端页面匹配处理这种数据才行。
- 在标准页面上,调用“cube__download”脚本。
为了简化配置,此处没有把脚本封装为公共接口,实际使用时业务应该使用公共接口进行封装,以便实现更细粒度的权限控制。
以jQuery的ajax方法为例,代码示例如下:
var url = "/u-route/baas/script/v1.0/run/cube__download"; context.$utils.getCSRFToken().then(function(token) { $.ajax({ type: "POST", headers: { 'Content-Type': "application/json", 'responseType':"arraybuffer", 'csrf-token': token, }, url: url, data: JSON.stringify({}), dataType: 'text', async: false, success: function(resp){ var fileName = "test" + '.xlsx'; var file = new Blob([s2ab(atob(resp))], {type: ''}); if (window.navigator.msSaveOrOpenBlob) { //IE浏览器下载 window.navigator.msSaveOrOpenBlob(file, fileName); } else { var fileUrl = URL.createObjectURL(file); var a = document.createElement('a'); a.href = fileUrl; a.target = '_blank'; a.download = fileName; document.body.appendChild(a); a.click(); } }, error: function(resp){ } }); });
“s2ab()”是数据处理的关键,这个方法名字是“string to array buffer”的缩写,意思是把字符串转换为ArrayBuffer类型,以便让前端可以处理二进制数据。其函数定义如下:
function s2ab(s) { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(buf); for (var i=0; i!=s.length; ++i) { view[i] = s.charCodeAt(i) & 0xFF; } return buf; }
上述代码中,需要注意如下事项:
- Header里面需要增加'responseType':"arraybuffer";
- Header里的类型为dataType: 'text';
- Header里增加csrf-token。