图书馆系列:文件-前端文件下载

138 阅读5分钟

写在顶部

急用代码的推荐只看:

  • 直接返回下载地址---方法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请求下载返回的数据截图: 这是axios请求下载返回的数据截图

这是设置响应类型为blob返回的具体数据截图: 这是设置响应类型为blob返回的具体数据截图

写在最后

其实个人喜欢是后端直接返回下载地址,然后直接a标签下载的,毕竟前端处理简单。而且当遇到下载文件大时,返回下载地址可以先让客户选择存储未知,然后慢慢下载,而文件流需要接口将所有数据返回之后,再走一步转化成url,最后才是a标签下载,如果文件大的话,等待的时间比较漫长。

不过下载地址安全性不够高,容易被外界滥用,而且现在也有分片下载了,也不算慢。加上我直接提供封装好的方法,前端处理也不用太多时间。

挖个坑,后续再写下分片下载的实现。

另外由于公司有时候也需要后端要写前端的页面,所以up主出的文章尽量简洁,方便,可ctrl+c加ctrl+v直接用。

最后的最后,如果大家有补充的欢迎评论,共同学习。