乐趣区

关于javascript:JS封装一个下载多种类型文件的方法

背景

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


一、残缺代码

为不便开发工作非常沉重的同学节省时间,残缺代码放在顶端便于 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' // 申请下载接口 url
const params = {id:1} // 参数:下载文件的 id
const 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() 办法。


三、总结

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

退出移动版