写在顶部
急用代码的推荐只看:
- 直接返回下载地址---方法1:建立a标签js点击下载
- 调用接口返回文件流数据---方法1:文件流转url,然后使用直接返回url的方式下载
最近
距离9月不加班还有2个月10天,四舍五入就是1个月10天,满打满算扣除睡觉吃饭就是13天33333333。听说上天眷顾努力的人,我问老天我现在去买彩票能够中500w吗?老天直接给我一个大嘴巴子,午休该醒了,起来上班。
前端下载相信基本所有前端都会遇到过,并实现过功能。下载一般有2种,直接返回下载地址,或者返回文件流,读取数据转化为正确的文件,不过下载主流都是走a标签下载。
直接返回下载地址
这种的一般多是图片、excel、word、视频等,且支持的文件范围广,基本上所有文件都可通过该方式进行下载。
方法1:建立a标签js点击下载(主打)
这里就写下下载的方法:
function download(url) {
if (getDataType(url) === 'string') return throw Error('url 格式错误');
const a = document.createElement('a'); // 创建a标签
a.href = url;
a.download = url.substr(url.lastIndexOf('/')+1) // 配置下载文件名,适用于url是以文件名格式结尾的。
a.click(); // 进行下载
a = null; // 释放内存
}
function getDataType(data) { // 判断数据格式
let type = Object.prototype.toString.call(data);
return type.replace(/([object )|(])/g, '').toLocaleLowerCase()
}
方法2:直接使用window.open(url)下载,这类仅支持下载浏览器不支持预览的文件(仅做了解,不推荐,但可以用来偷懒实现预览音视频、pdf、图片等)
window.open(url); // 谷歌浏览器会直接下载
以下可以不看,主要是up主测试window.open()的支持度:
up主的浏览器版本:ie-11.572,谷歌-93.0,火狐-102.0。
<span style="color:red">音视频、pdf、图片</span>均能**预览**。
<span style="color:red">docx、xlsx、ppt、以及zip压缩类文件</span>均只能**下载**。
如果打开window.URL.createObjectURL(file)转化的地址,比如这样的:`blob:http://localhost:8081/4ba22743-8e04-4d8f-8f24-2eeeddcf029d`,存在部分可正常浏览,一般多为图片、视频、pdf这类原本能预览的,部分比如markdown文件,原本能预览,现在就不行了。
如果打开图片转化的base64地址,无法正常预览。
调用接口返回文件流数据
方法1:文件流转url,然后使用直接返回url的方式下载
async function downloadByBlob(url, param) {
const res = await axios.post(url, param, {
responseType: 'blob',
headers: {
'Content-Type': 'application/json' // 告知后端请求参的类型
},
withCredentials: true // 表示跨域请求时是否需要使用凭证
});
const blob = new Blob([res.data])
// 从响应头中获取文件名以及文件类型
// 响应头里还会返回content-type,代表文件的格式类型,如果值是我下方截图中的:application/octet-stream,这个是通用文件流
// 还有更精准的比如:application/pdf,就是pdf格式。如果你想了解更多这个类型参考:http://t.zoukankan.com/cuteCoderSnow-p-13957874.html
const typeStr = res.headers['content-disposition'];
// 一般来说文件流下载,后端响应头中会返回文件名以及文件格式,在content-disposition中,
// 比如这样的:"attachment; filename=\"1011002911.zip\"",其中attachment表示以附件形式下载,
// 它还可以写成inline,直接在页面展示。
// 如果没有,那么就自己命名文件,或者按照设计需求命名,且需要自己知道下载文件格式,并写明。
const fileName = typeStr ? typeStr.substr(typeStr.indexOf('=')+1).replace(/\"/g, '') : `${new Date().getTime()}.zip`;
// 实现下载
if (window.navigator.msSaveOrOpenBlob) {
// ie10以上包含10,支持window.navigator.msSaveOrOpenBlob
navigator.msSaveBlob(blob, fileName);
} else {
let a = document.createElement('a');
a.href = window.URL.createObjectURL(blob);
a.download = fileName;
a.click();
// 释放内存
window.URL.revokeObjectURL(a.href);
a = null;
}
}
此方法我用axios演示,使用的post请求,可根据实际需要,修改请求插件或请求方式,其中这么几个重点:
- 请求配置中必须有
reponseType:blob用于告知后端我希望获取blob文件流。 - 正常来说如果是文件下载,后端返回的响应头中必定有这两个属性(如果后端忘记配置了,那就考虑自己生成文件名,且需要知道自己下载的文件是什么格式,直接配置上去):
content-type: "application/octet-stream以及content-disposition: "attachment; filename=\"1011000291.zip\"",
这是axios请求下载返回的数据截图:
这是设置响应类型为blob返回的具体数据截图:
写在最后
其实个人喜欢是后端直接返回下载地址,然后直接a标签下载的,毕竟前端处理简单。而且当遇到下载文件大时,返回下载地址可以先让客户选择存储未知,然后慢慢下载,而文件流需要接口将所有数据返回之后,再走一步转化成url,最后才是a标签下载,如果文件大的话,等待的时间比较漫长。
不过下载地址安全性不够高,容易被外界滥用,而且现在也有分片下载了,也不算慢。加上我直接提供封装好的方法,前端处理也不用太多时间。
挖个坑,后续再写下分片下载的实现。
另外由于公司有时候也需要后端要写前端的页面,所以up主出的文章尽量简洁,方便,可ctrl+c加ctrl+v直接用。
最后的最后,如果大家有补充的欢迎评论,共同学习。