方案一:a 标签加 download 属性

这种情况下,必须保证 url 是处于同源的情况下,跨域时 download 属性是不生效的。download 属性指示浏览器该下载而不是打开该文件,同时该属性值即下载时的文件名;

例:

1
2
<a href="url" download="filename"/>

方案二:通过后端设置,设置下载的请求头为 Content-Disposition 使其强制让浏览器进行下载,这种方式不受跨域的影响 例:Content-Disposition: attachment; filename=”filename.jpg”

在常规的 HTTP 应答中,该响应头的值表示对响应内容的展现形式:

1.inline 表示将响应内容作为页面的一部分进行展示

2.attachment 表示将响应内容作为附件下载,大多数浏览器会呈现一个“保存为”的对话框

3.filename(可选) 指定为保存框中预填的文件名

这种情况下 使用 window.open()或者 a 标签不设置 download 属性都是可以让浏览器直接下载的

方案三:通过接口返回文件流的形式进行下载

当我们的请求已解决跨域的问题(如 nginx 或使用 axios 等),可以直接通过请求的方式拿到文件流,将文件流转为 blob 格式,再通过 a 标签的 download 属性下载。下面用 axios 进行举例

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
// get请求
this.axios.get('/api/downloadConfig', {
params:{oid:oid},
responseType:'blob',
})
// post请求
this.axios.post('/api/downloadConfig', form, {responseType:'blob'})

// 返回结果处理
if(res.status === 200){
const content = res.data;
const blob = new Blob([content]);
if('download' in document.createElement('a')){
//非IE下载
const a = document.createElement('a');
a.download = fileName;
a.style.display = 'none';
a.href = window.URL.createObjectURL(blob);
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(a.href);
document.body.removeChild(a);
}else{
//IE10+下载
if(typeof window.navigator.msSaveBlob !== 'undefined'){
window.navigator.msSaveBlob(blob, _this.selected);
}else{
let URL = window.URL || window.webkitURL;
let downloadUrl = URL.createObjectURL(blob);
window.location = downloadUrl;
}
}

使用 xhr 也是可以的,例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const xhr = new XMLHttpRequest();
xhr.open('GET', '/upload/user.png', true);
xhr.responseType = 'blob';
xhr.onload = function() {
if (this.status === 200) {
const fileName = 'test.jpg';
const blob = new Blob([this.response]);
const blobUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = blobUrl;
a.download = fileName;
a.click();
window.URL.revokeObjectURL(blobUrl);
}
};
xhr.send();

特别说明:

当用方案一二实现时,下载文件名优先取的是 Content-Disposition 的 filename 而不是 download,而通过 blob 的形式,文件名优先取的 download 属性,如果都没有设置则取的 url 最后一节;

当通过接口的形式访问文件,又想保留浏览器的预览效果时,可以仅设置 Content-Disposition 的 filename 以指定预览时下载的文件名,否则浏览器会默认取 url 最后一节,即 downloadfile 为文件名,导致下载的文件无后缀无法打开

window.open() 和 a 标签 执行的是打开链接的操作,类似于将地址直接输入到浏览器中,相当于从一个域跳到另一个域,因此 window.open(‘http://xxx')可以访问而不会报跨域错误;

浏览器取下载时文件名的优先级是 Content-Disposition: filename=“文件名.png” 优先于 优先于 url 最后一节 http://localhost:8087/upload/文件名.png