在前端常见的业务场景中, 下载场景是很普遍的一种业务需求,前端下载方式非常多,每种方式各有适用它的场景。 下面针对最常见的前端下载场景, 做了使用比较, 方便大家在合适的场景中, 选择最适合的下载方式。
window.open/location.href
非常常见的前端下载方式, 这种方式为后端生成文件,将文件直接传递到前端来,通常称之为后端下载. 适用于文件由后端生成的情况
window.open('http://localhost:3000/file') // 后端需要设置文件下载头部, 才能正常下载 Content-Disposition: attachment;
// 参数携带
window.open('http://localhost:3000/file?name=lisi')
location.href = 'http://localhost:3000/file'
location.href = 'http://localhost:3000/file?name=lisi'
劣势:
-
请求方式只能是 get , 因此数据的携带量较小, 且安全性较低, 对于需要携带数据量稍微大一点的请求不适用
-
在异步情况下使用时, 会受到浏览器安全策略的拦截, 因为是通过脚本进行的窗口打开,可能会被认为是广告或者有安全问题,拦截弹窗或者询问是否允许下载。
const Title = () => { const fun = () => { setTimeout(() => { window.open('http://localhost:3000/file?name=123') }, 1000) } return <h1 onClick={fun}>下载文件</h1> } <Title/>
优势:
- 使用方便, 可定义打开在窗口或者当前窗口
- 没有跨域问题, 不同域下依然能够使用
iframe
创建 iframe 这种文件下载的方式在实际生产过程中其实不太常用, 原理类似 window.open/location.href, 只是使用方式上略有不同, 大多数的情况下, 大家会选择使用 a 链接去下载文件
// 使用 iframe
let iframe = document.createElement('iframe');
iframe.style.display = 'none';
iframe.src = 'http://localhost:3000/file?name=lisi'
document.body.appendChild(iframe);
劣势:
- get 请求方式, 携带数据量有限
优势:
- 没有跨域问题, 不同域下依然能够使用
a链接
创建 a 链接,或者使用 a 标签直接设置 download 属性进行下载,这种方式是前端最普遍使用的方式。
// 创建 a 链接
let dom = document.createElement('a');
dom.style.display = 'none';
dom.download = '';
dom.href = 'http://localhost:3001/file?name=123'
document.body.appendChild(dom);
dom.click();
document.body.removeChild(dom);
// 或者直接在 a 链接上使用 download 属性
<a href='http://localhost:3001/file?name=123' download >
劣势:
- get 请求方式, 携带数据量有限
优势:
- 没有跨域问题, 不同域下依然能够使用
URL.createObjectURL
这种前端的下载处理方式为现代浏览器新增的使用方式, 但是好消息是几乎大部分的浏览器现在都支持, 这种方式为纯前端的下载方式, 文件在前端生成, 然后浏览器下载前端生成的文件。
当调用 createObjectURL() 时, 会生成一个指向指定 file 对象或者 Blob 的 URL, 得到 URL 后, 使用 下载方式 3 进行文件下载
const type = "application/json", name = "file.json";
const data = 'download file';
const blob = new Blob([data], {type});
const url = window.URL.createObjectURL(blob);
// 使用完毕后,安全的时间点清除 URL
window.URL.revokeObjectURL(url);
或者我们在通常的使用中,文件非前端产生, 而是通过后端传递文件流
fetch('http://localhost/file')
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'example.json';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
劣势:
- 文件生成方式为前端, 仅适用文件大小比较小的文件, 大文件会造成前端性能问题
- 在 IE 浏览器环境下 URL.createObjectURL 有兼容性问题
- 和服务器交互时, 有跨域问题
优势:
- 纯前端下载时, 不用和后端交互, 使用起来方便
- 和服务器有交互时, 传递的数据方式可以为 get/post 等, 携带的数据量比较比较大
Form Post
这种下载方式使用原生的 form ,通过表单 submit, 传递数据到服务器端
const downloadForm = document.createElement('form');
downloadForm.method = 'post';
downloadForm.target = '_blank';
downloadForm.hidden = true;
downloadForm.action = url;
// 这里可以携带数据, 但是携带的数据,必须得通过 表单元素的方式附加到 form 里面
document.body.appendChild(downloadForm);
downloadForm.submit();
document.body.removeChild(downloadForm);
劣势:
-
使用时稍微有点麻烦, 数据需要添加到 form 里面
-
异步场景里面使用时会被安全策略阻止下载
setTimeout(() => { // 在这里使用 form post 下载 }, 1000)
优势:
- 没有跨域问题
- 可以通过 post 传递更多数据