4

background

I remember that day, the product found me: "We want to create a function that can download various types of files, what do you think?
Me: "Well, no problem, I can do it!" (Inner: "I have to do it~ What can I do")
So, there is today’s little sharing (Xiaobai’s first article)


1. Complete code

order to save time for students with very heavy development tasks, the complete code is placed on the top to facilitate cv Dafa.

  1. function
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. Call method
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
    }
})

2. Detailed analysis

File Title FileName

Due to different business scenarios, the fileName file name is passed in from the front end. Normally, the fileName should be obtained through the XHR.getResponseHeader('content-disposition') method; however, there are a few precautions for this:

1. Call XHR.getResponseHeader('content-disposition') to report an error: (Refused to get unsafe header "Content-Disposition")

disposition报错信息
solution: requires background cooperation to add context.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition").
specific content of 16083d7122eb84 can be viewed: you really use XMLHttpRequest?
2. Chinese garbled characters in the acquired fileName:

  • Method 1: response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode(fileName, "UTF-8"));
  • method two: response.setHeader( "Content-Disposition", "attachment;filename=" + new String( fileName.getBytes("gb2312"), "ISO8859-1") );
  • Method Three: [Node.js] Use iconv-lite to solve Chinese garbled

specific content of 16083d7122ec3b can be viewed: Set the Content-Disposition property of the response to realize the file download example

download progress onProgress

When downloading large files, you usually want to prompt the user with a download progress function, so we use the XHR.onprogrees() method to monitor the current download progress:

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)
    }
}

above code uses xmlhttp.getResponseHeader('Content-length') to get the file size, which is really helpless; originally the event in the callback of the onprogress function will return loaded (current download size) and total (file size), but The total I monitored locally is always 0; after consulting the information, I learned that it is related to the accept-encoding: gzip in the request header; I blamed me for being inferior, and I hope that the big guys I know can answer questions and provide solutions for me, thank you! Thank you! Thank you!

compatibility processing

has not paid attention to the download compatibility issue of Firefox before, and the result was pointed out by our test lady. A coquettish meal allows me to solve it,~~~Who can stand this; then help her solve it, after all, I am a good person-.-!
problem description: Firefox browser (except version 86.0) will have no suffix when calling this method to download, causing the downloaded file type to be unclear and unable to open.
reason for the not clear yet; (Wang Dashen told me) I have consulted a lot of information and say that it is because there are spaces in the file name that the Firefox browser directly truncates the content after the spaces when downloading and parsing; but my file has no spaces. . .
solution: getBrowserInfo() method + addNameSuffix() method in the above code.


III. Summary

In fact, it is not difficult to write a download method, but I found that if you really want to share it, a lot of things have to be studied in depth. The reference links used in the article have been given, and they are all useful articles. Please read it yourself~ !


D_c
7 声望2 粉丝