Ajax处理文件流下载

发布时间 2023-11-30 17:18:57作者: DreamerSix
/*
* AJAX请求 后端返回文件流下载处理
* @param {object} option 下载请求选项配置
* @param {string} option.method 请求方式 POST|GET etc.
* @param {string} option.url 请求地址
* @param {number} option.timeout 请求超时时间 单位:毫秒
* @param {function} option.beforesend 请求发送前回调
* @param {function} option.ontimeout 请求超时回调
* @param {function} option.onprogress 请求响应体下载进度回调
* @param {function} option.innererror 接口内部自定义错误回调
* @param {function} option.outererror 接口外部异常回调 状态码非200的异常
* @param {function} option.error 接口异常回调
* @param {function} option.onresponsestart 响应开始传输回调
* @param {function} option.complete 响应传输结束|请求结束 回调
*/
function download(option){
    var xhr=new XMLHttpRequest();
    xhr.responseType='blob';//只有异步请求才能设置responseType,否则报错,所以这里只能是异步(存疑)
    xhr.open(option.method, option.url, true); //打开连接

    //请求发生之前回调
    if(typeof option.beforesend==='function')
    {
        option.beforesend(xhr);
    }

    //下载请求超时设置
    if(typeof option.timeout==='number')
    {
        xhr.timeout=option.timeout;
    }

    //下载超时回调
    xhr.ontimeout=typeof option.ontimeout==='function'?option.ontimeout:
    function(e){
        console.error('request timeout.');
    };

    
    //下载进度回调
    if(typeof option.onprogress==='function')
    {
        xhr.onprogress=option.onprogress;
    }

    //请求成功回调
    xhr.onload=function(){
        if(this.status===200)
        {
            var data = this.response;
            if(data.type.indexOf('application/json')>-1){
                var reader=new FileReader();
                reader.readAsText(data,'UTF-8');
                reader.onload=function(){
                    try{    
                        var res=JSON.parse(reader.result);
                        typeof option.innererror==='function'?
                        option.innererror(res):
                        console.error(res);
                    }
                    catch(e){
                        console.error(e);
                    }
                }
            }
            else
            {
                 /*
                 获取后台的下载文件名:如果跨域时Content-Disposition默认前端不给读取,需要后端API操作暴露给前端
                 */

                var disposition = decodeURI(xhr.getResponseHeader("Content-Disposition"))
                ,filename=option.filename||getFilenameFromDisposition(disposition)
                ,mimetype=xhr.getResponseHeader("Content-Type")
                
                //保存为文件下载
                saveAsFile(data, filename,mimetype);
            }
        }
        else
        {
            typeof option.outererror==='function'?option.outererror(xhr.status,xhr.statusText,xhr.response):
             console.error(xhr.status,xhr.statusText,xhr.response);
        }
    }

    //请求异常时触发,一般网络状态异常不通时会触发,例如:网络断开、无法连接目标服务器等
    xhr.onerror=typeof option.error==='function'?option.error:
    function(e){
        console.error('an error has occured.');
    };

    //响应数据开始传输
    if(typeof option.onresponsestart==='function')
    {
        xhr.onloadstart=option.onresponsestart;
    }
   

    //响应数据传输完成
    if(typeof option.complete==='function')
    {
        xhr.onloadend=option.complete;
    }
    
    //发生请求数据
    xhr.send(option.payload);
}

/*
* 将二进制流数据转换成具体文件下载
* @param {blob} data 二进制流数据
* @param {string} filename 文件下载名称
* @param {string} mimeType 文件MIME类型
*/
function saveAsFile(data,filename,mimeType)
{
     //兼容ie
    if ('msSaveOrOpenBlob' in navigator) 
    {
        var blob = new Blob([data], { type: mimeType });
        window.navigator.msSaveOrOpenBlob(blob, filename);
    } 
    else 
    {
        var blob = new Blob([data], { type: mimeType });
        var url = window.URL.createObjectURL(blob);
        var link = document.createElement('a');
        document.body.appendChild(link);
        link.style.display = 'none';
        link.download = filename;
        link.href = url;
        link.click();
        window.URL.revokeObjectURL(url);//手动释放blobURL,避免内存溢出 
        document.body.removeChild(link);
    }
}

/*
 * 获取文件下载名称
 * @param {string} disposition 请求响应头中的Content-Disposition
 */
function getFilenameFromDisposition(disposition){
    var filename='';
    if (disposition && disposition.indexOf('attachment') !== -1) {
        var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        var matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) {
            filename = matches[1].replace(/['"]/g, '');
        }
    }

    return filename;
}