文件下载方法很多,开发过程也走了几个坑,特别是部署到测试环境后,出现了本地开发不曾出现过的问题,本篇记录一下各种写法和实际问题解决。
文件格式
后端读取文件后以流的形式传给我,从前端的角度理解就是Blob形式。
打开浏览器调试工具,resonpse需要携带这样的头:
注意在请求接口里面需要添加一个responseType
1 2 3 4 5 6 7 8 9 10
| export function downloadFile(url: string, json: object) { return axios({ url, method: "post", data: json, responseType: "blob", }) .then((res) => res) .catch((error) => error); }
|
使用前端直接下载
在表格row上绑定导出方法,并将数据传入。
1 2 3 4 5 6 7
| import Export from "../../utils/export"; const handleExport = (index, row) => { let data = []; data[0] = JSON.parse(JSON.stringify(toRaw(row))); console.log("export", data); Export(data, fields, "导出名字"); }
|
export.ts
中提前配置好
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| import fs from "file-saver"; import * as XLSX from "xlsx"; export default (json, fields, filename = "测试数据.xlsx") => { json.forEach((item) => { for (const i in item) { if (fields.hasOwnProperty(i)) { item[fields[i]] = item[i]; } delete item[i]; } });
const sheetName = filename; const wb = XLSX.utils.book_new(); const ws = XLSX.utils.json_to_sheet(json, { header: Object.values(fields) }); wb.SheetNames.push(sheetName); wb.Sheets[sheetName] = ws; const defaultCellStyle = { font: { name: "Verdana", sz: 13, color: "FF00FF88" }, fill: { fgColor: { rgb: "FFFFAA00" } }, }; const wopts = { bookType: "xlsx", bookSST: false, type: "binary", cellStyles: true, defaultCellStyle: defaultCellStyle, showGridLines: false, }; const wbout = XLSX.write(wb, wopts); const blob = new Blob([s2ab(wbout)], { type: "application/octet-stream" }); fs.saveAs(blob, filename + ".xlsx"); }; const s2ab = (s) => { if (typeof ArrayBuffer !== "undefined") { const buf = new ArrayBuffer(s.length); const view = new Uint8Array(buf); for (let i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff; return buf; } else { const buf = new Array(s.length); for (let i = 0; i != s.length; ++i) buf[i] = s.charCodeAt(i) & 0xff; return buf; } };
|
使用后端接口获取Blob数据下载
文件使用Blob流表示。
1 2 3 4 5 6 7 8 9 10 11 12
| const handleExport = (index, row) => { const ids = []; ids.push(row.id); exportBatchStandard({ ids: ids }) .then((res) => { fileDownloadFun(res); }) .catch((error) => { ElMessage.error("导出错误!"); console.log(error) }); };
|
index.ts
中两种下载方式
方法一:使用fileDownload
库
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| export const fileDownloadFun = (res: any, name?: string) => { if (name) { fileDownload(res.data, name) } else { let fileName = '' let contentDisposition = '' if (res.headers['content-disposition']) contentDisposition = res.headers['content-disposition'] if (res.headers['Content-Disposition']) contentDisposition = res.headers['Content-Disposition'] const result = contentDisposition.split("filename*=utf-8''")[1] console.log('result=', result) if (result == undefined) { ElMessage.error('下载文件失败') } else { fileName = decodeURIComponent(result) console.log('download=', res.data, 'file=', fileName) fileDownload(res.data, fileName) } } }
|
方法二:使用标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| export const download = (res: any, name?: string) => { const data = res.data; if (!res.data) { return; } const pat = new RegExp("(?<=filename=).*"); let contentDisposition = ""; if (res.headers["content-disposition"]) contentDisposition = res.headers["content-disposition"]; if (res.headers["Content-Disposition"]) contentDisposition = res.headers["Content-Disposition"]; const result = pat.exec(contentDisposition); let fileName = result && result[0]; if (fileName == undefined) { fileName = name; } else { fileName = decodeURIComponent(fileName); } const url = window.URL.createObjectURL(new Blob([data])); const link = document.createElement("a"); link.style.display = "none"; link.href = url; link.setAttribute("download", fileName); document.body.appendChild(link); link.click(); };
|
注意:
- 一定要让后端添加暴露header的字段,不然前端可能获取不到
content-disposition
(本地环境代理没问题,提交到线上环境出现了问题)
- 约定好formdata格式,前端可能需要添加hearder(根据框架封装程度而定)
1 2
| response.setHeader("Access-Control-Expose-Headers", "Content-Disposition") response.setHeader("Content-Disposition", ...)
|