一、直接使用<a>标签下载
如果文件部署到了服务器上,可以通过接口拿到文件的url,直接使用<a>标签进行下载。
<a href="/user/test/xxxx.xls"` `download="文件名.xls">点击下载</a>
注意:
- 这种方法的弊端就是需要网站与资源url同源,跨域的话会跳转至新页面并预览,例如下载的如果是视频会打开新窗口进行播放,音频也会打开新窗口进行播放,txt/doc/pdf/jpg同理,如果下载的是游览器不支持,才会下载。
- 非同源的文件可以告知用户使用鼠标右键调用迅雷、IDM之类的第三方软件进行下载
二、如果文件是通过接口返回
1、Blob对象
Blob对象表示一个不可变、原始数据的类文件对象。Blob 表示的不一定是JavaScript原生格式的数据。File接口基于Blob,继承了blob的功能并将其扩展使其支持用户系统上的文件。
(1)构造函数
var aBlob = new Blob( array, options );
array 是一个由ArrayBuffer(二进制数据缓冲区)、ArrayBufferView(二进制数据缓冲区的array-like视图)、Blob、DOMString等对象构成的Array,或者其他类似对象的混合体,它将会被放进Blob。DOMStrings会被编码为UTF-8。
options 是可选的,它可能会指定如下两个属性:
-
type,默认值为 "",它代表了将会被放入到blob中的数组内容的MIME类型,下载指定扩展名的文件只需要对照MIME 参考手册设置type即可。
-
endings,默认值为"transparent",用于指定包含行结束符n的字符串如何被写入(一般不使用)。 它是以下两个值中的一个: "native",代表行结束符会被更改为适合宿主操作系统文件系统的换行符,或者 "transparent",代表会保持blob中保存的结束符不变。
(2)示例
var debug = { hello: "world" };
var blob = new Blob([JSON.stringify(debug)], { type : 'application/json' });
2、URL对象
(1)通过创建URL对象指定文件的下载链接。
objectURL = window.URL.createObjectURL(blob);
(2)window.URL.revokeObjectURL()
在每次调用createObjectURL()方法时,都会创建一个新的 URL 对象,即使你已经用相同的对象作为参数创建过。当不再需要这些 URL 对象时,每个对象必须通过调用 URL.revokeObjectURL()方法来释放。浏览器会在文档退出的时候自动释放它们,但是为了获得最佳性能和内存使用状况,你应该在安全的时机主动释放掉它们。
window.URL.revokeObjectURL(objectURL);
3、利用<a>
标签下载
*没必要将<a>标签添加到dom中(document.body.appendChild(link))
(1)生成一个<a>标签。
const link = document.createElement('a');
(2)href属性指定下载链接
link.href = window.URL.createObjectURL(blob);
(3)dowload属性指定文件名
download 属性规定被下载的超链接目标。在<a>
标签中必须设置 href 属性。该属性也可以设置一个值来规定下载文件的名称。所允许的值没有限制,浏览器将自动检测正确的文件扩展名并添加到文件 (.img, .pdf, .txt, .html, 等等)。
link.download = fileName;
(4)click()事件触发下载
link.click();
4、格式转换
MIME类型使用text/plain,用.txt文件的格式编码去下载doc(docx)文件(doc(docx)文件每次打开需要选择合适的编码,暂未找到解决方案,欢迎补充)。
const foo = { hello: "world" };
const blob = new Blob([JSON.stringify(foo)], { type: "text/plain" });
const fileName = `${new Date().valueOf()}.doc`;
const link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = fileName;
// 没必要将<a>标签添加到dom中:document.body.appendChild(link)
link.click();
window.URL.revokeObjectURL(link.href);
5、请求失败处理
axios请求下载导出一个文件,请求成功时返回的是一个流形式的文件,需要设置responseType: 'arraybuffer',但是请求失败的需要返回的是json数据,所以需要把arraybuffer转成Json对象。
例如:
请求设置了responseType: 'arraybuffer',
请求成功时,下载文件,
请求失败时,后端返回json对象,如:{"msg":"系统异常","code":1,"success":false},也被转成了arraybuffer,
解决方案是,失败时,将数据arraybuffer转成Json对象就好了。
实例一(使用接口反参来判断失败)
根据接口返回type来判断调用成功,如果是 'application/json' 返回json字符串则说明调用失败。
(要和后端达成一致)
// 点击后触发
onClickDownload = (id, fileName) => {
const options = {
url: 'xxxxxx',
method: 'get',
data:{ id },
responseType: 'blob', // 也可以使用arraybuffer,跟后端达成一致
};
axios(options).then(res => {
const blob = new Blob([res]);
// 接口失败处理,使用哪个字段主要看接口定义
if (res.type === 'application/json') {
const reader = new FileReader(); // 使用FileReader对象来读取文件
// 文件读取成功完成时触发
reader.onload = () => {
const resData = JSON.parse(reader.result);
if (resData.code === -999 || resData === -998) {
// 如果是 -999/-998 说明登录失效跳转到登录页面(根据接口定义来)
window.location.href = 'https://www.xxx/login';
} else {
// 其他错误使用antd的message给用户提示
message.error(resData.message);
}
};
reader.readAsText(blob); // 将文件读取为文本
} else {
const link = document.createElement('a');
link.href = URL.createObjectURL(blob); // window进行了省略(window.URL)
// 这里就不存在跨域问题,因为文件内容前端自已进行了缓存,href是自己生成的
link.download = fileName; // 设置文件名
linck.click(); // 触发下载
URL.revokeObjectURL(link.href); // 释放URL对象
}
});
}
实例二(使用try-catch捕获错误)
处理这种特殊返回值时需要设置axios的responseType为blob,防止axios内部默认处理返回值
axios.get({
url: 'xxxxxx',
method: 'get',
data:{},
responseType: 'blob'
}).then(res => {
let data = res.data;
let fileReader = new FileReader();
fileReader.onload = function() {
try {
let jsonData = JSON.parse(this.result);
if (jsonData.code) {
// 说明是普通对象数据,后台转换失败
// to do something
alert('not ok');
}
} catch (err) {
// 解析成对象失败,说明是正常的文件流
alert('ok');
}
};
fileReader.readAsText(data);
});
实例三(URL 转成 Blob 进行下载)
const dataURLToBlob = (dataURL, type) => {
// 代码参考自:https://github.com/ebidel/filer.js
const parts = dataURL.split(';base64,');
const contentType = parts[0].split(':')[1]; // 原始类型
const raw = window.atob(parts[1]);
const rawLength = raw.length;
const uInt8Array = new Uint8Array(rawLength);
for (let i = 0; i < rawLength; i++) {
uInt8Array[i] = raw.charCodeAt(i);
}
return new Blob([uInt8Array], { type: type || contentType });
};
const download = (dataURL, filename) => {
const blob = dataURLToBlob(dataURL);
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style = 'display: none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
};
三、使用相关插件进行下载
downloadjs、github搜索download