背景

记得那一天,产品找到我:“咱们要做一个能够下载各类型文件的性能,你有什么想法?”
我:“嗯,没问题,我都行!~”(心田:“我得做呀~我能怎么办呐”)
于是,就有了明天这篇小分享(小白的第一篇文章)


一、残缺代码

为不便开发工作非常沉重的同学节省时间,残缺代码放在顶端便于cv大法。

  1. 函数
import store from '../store'/**  *获取浏览器类型及版本 *(调用此办法判断浏览器类型及版本,解决火狐浏览器下载的文件没有后缀名的问题) */function getBrowserInfo() {    var Sys = {}    var ua = navigator.userAgent.toLowerCase()    var re = /(msie|firefox|chrome|opera|version).*?([\d.]+)/    var m = ua.match(re)    Sys.browser = m[1].replace(/version/, "'safari")    Sys.ver = m[2]    return Sys}/**  *依据application类型获取后缀名称 *(解决火狐浏览器下载的文件没有后缀名的问题) */function addNameSuffix(type) {    let suffixName = ''    switch (type) {    case 'application/vnd.openxmlformats-officedocument.wordprocessingml.document':        suffixName = '.docx' // docx        break    case 'application/pdf':        suffixName = '.pdf' // pdf        break    case 'application/zip':        suffixName = '.zip' // zip        break    case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet':        suffixName = '.xlsx' // '' xlsx        break    case 'application/vnd.openxmlformats-officedocument.presentationml.presentation':        suffixName = '.pptx' // pptx        break    case 'application/vnd.ms-excel':        suffixName = '.xls' // xls        break    case 'application/msword':        suffixName = '.doc' // doc        break    case 'application/vnd.ms-powerpoint':        suffixName = '.ppt' // ppt        break    }    return suffixName}/** * @param {string} url:后端接口地址 * @param {Object} params:申请参数 * @param {string} fileName:文件名称 * @returns {Object} */function downloadFile(url, params = null, fileName = '数据下载') {    return new Promise((resolve, reject) => {        try {            let xmlhttp            if (window.XMLHttpRequest) {                xmlhttp = new XMLHttpRequest()            } else {                xmlhttp = new ActiveXObject('Microsoft.XMLHTTP')            }            fileName = fileName.replace(/\./g, '-') //解决文件名中的英文.会导致下载文件类型谬误            xmlhttp.withCredentials = true // 跨域申请携带cookie            xmlhttp.responseType = 'arraybuffer'            xmlhttp.open('POST', url, true)            xmlhttp.setRequestHeader('Content-type', 'application/json;charset=UTF-8')            xmlhttp.setRequestHeader('token', store.state.user.token)            xmlhttp.setRequestHeader('currentTimeMillis', store.state.user.currentTimeMillis)            if (params) {                params = JSON.stringify(params)            }            xmlhttp.send(params)            xmlhttp.onreadystatechange = () => {                if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {                    if (xmlhttp.response) {                        // 后端返回content-type格局为:application/************文件类型 */;charset=UTF8                        const applicationConfig = xmlhttp.getResponseHeader('content-type').split(';')[0]                        // 火狐浏览器非86版本解决下载增加后缀名称                        const sys = getBrowserInfo()                        if (sys.browser == 'firefox' && sys.ver != '86.0') {                            const suffix = addNameSuffix(applicationConfig)                            fileName = fileName + suffix                        }                        const content = xmlhttp.response                        const url = window.URL.createObjectURL(new Blob([content], { type: applicationConfig }))                        const link = document.createElement('a')                        link.style.display = 'none'                        link.href = url                        link.setAttribute('download', decodeURIComponent(fileName))                        document.body.appendChild(link)                        link.click()                        document.body.removeChild(link) // 下载实现移除元素                        window.URL.revokeObjectURL(url) // 开释blob对象                        resolve(true)                    }                }            }            xmlhttp.onprogress = (event) => {                const total = xmlhttp.getResponseHeader('Content-length')                const percent = ((event.loaded / total) * 100).toFixed(2)                console.log(`下载进度:${percent}`)                if (event.loaded == total) {                    resolve(true)                }            }        } catch (e) {            reject(e)        }    }) }
  1. 调用形式
const url = this.settings.httpService + 'autonomyreport/downloadreport.do' // 申请下载接口urlconst params = {id:1} // 参数:下载文件的idconst fileName = '我是被下载的文件名称'downloadFile(url, params, fileName).then(flag => {    if(flag){        // 下载胜利 do something    }})

二、具体解析

文件题目FileName

因业务场景不同,fileName文件名称由前端传入,失常状况是应该通过XHR.getResponseHeader('content-disposition')办法去获取fileName;然而这种有几点注意事项:

1. 调用XHR.getResponseHeader('content-disposition')报错:(Refused to get unsafe header "Content-Disposition")


解决形式:须要后盾配合增加context.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition")。
具体内容可查看:你真的会应用XMLHttpRequest吗?
2. 获取的fileName中文乱码问题:

  • 办法一:response.setHeader(”Content-Disposition”, “attachment; filename=” + java.net.URLEncoder.encode(fileName, “UTF-8″));
  • 办法二:response.setHeader( “Content-Disposition”, “attachment;filename=” + new String( fileName.getBytes(”gb2312″), “ISO8859-1″ ) );
  • 办法三:【Node.js】应用iconv-lite解决中文乱码
  • 具体内容可查看:设置response的Content-Disposition属性,实现文件下载示例

下载进度onProgress

在下载大文件的时候个别会想要给用户提醒一个下载进度的性能,这样咱们就用到了XHR.onprogrees()办法来监听以后的下载进度:

xmlhttp.onprogress = (event) => {    const total = xmlhttp.getResponseHeader('Content-length')    const percent = ((event.loaded / total) * 100).toFixed(2)    console.log(`下载进度:${percent}`)    if (event.loaded == total) {        resolve(true)    }}

上述代码中应用xmlhttp.getResponseHeader('Content-length')来获取了文件大小,实属无奈之举;原本onprogress函数的回调中event会返回loaded(以后下载大小)和total(文件大小),然而我本地监听到的total始终为0;查阅材料后得悉与申请头中的accept-encoding: gzip相干;怨我才学浅陋,望通晓的大佬能够为我答疑解惑,提供解决方案,谢谢!谢谢!谢谢!

兼容性解决

之前始终没关注火狐浏览器的下载兼容问题,后果被咱们测试小姐姐点进去了。一顿撒娇让我解决,~~~这谁受得了呀;那就帮她解决解决吧,毕竟我是个坏蛋 -.-!
问题形容:火狐浏览器(除86.0版本)在调用此办法下载时会呈现无后缀名的状况,导致下载的文件类型不明确无奈关上。
问题起因:尚不明确;(望大神告知) 查阅过很多材料都说是因为文件名中有空格导致火狐浏览器下载解析时间接截断空格后的内容了;但我的文件没有空格。。。
解决方案:如上代码中getBrowserInfo()办法+addNameSuffix()办法。


三、总结

其实写一个下载办法不难,但我发现真的要把它分享进去很多货色都得往深钻研钻研,文中用到的参考文献连贯曾经给出,都是一些有用的文章,大家自行浏览吧~!