XMLHttpRequest实现异步下载

背景

项目需要实现在下载Excel时,实时显示文件下载的进度条,不能直接调用浏览器下载。因此超链接下载,或者调用window.location.ref等方法无法满足要求,因此只有采用异步的方式。

为什么要用XMLHttpRequest

jQuery的AJAX是可以实现文件的异步上传,但无法实现异步下载,因此我们需要用到JS的XMLHttpRequest对象来实现.

XMLHttpRequest实现异步下载,我们需要将xhr的响应类型设置为blog(responseType = "blob"),在xhr的load事件中处理响应,并实现文件的保存。

下载的JS代码

jQuery(function() {
    jQuery("#DI_0027_EV02").click(function() {
        var xhr = new XMLHttpRequest();
        var url =  contextPath + DI_GUI_0027_02 + "?eventId=DI_0027_EV02&dataType="+dataType+"&deleteMode=02";
        xhr.open("GET",url);
        xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
        xhr.responseType = "blob";
        xhr.addEventListener("loadstart", function(ev) {
            // 开始下载事件:下载进度条的显示
            jQuery('div.progress-bar').css('width',"0%").find("span").text("0/0");
            jQuery('#progressModal').modal('toggle');
        });
        xhr.addEventListener("progress", function(ev) {
            // 下载中事件:计算下载进度
            var max   = ev.total;
            var value = ev.loaded;
            var width = value/max*100;
            jQuery('div.progress-bar').css('width',width+"%").find("span").text(value+"/"+max);
        });
        xhr.addEventListener("load", function(ev) {
            // 下载完成事件:处理下载文件
            processRequest(xhr);
        });
         xhr.addEventListener("loadend", function(ev) {
            // 结束下载事件:下载进度条的关闭
            jQuery('#progressModal').modal('toggle');
        });
        xhr.addEventListener("error", function(ev) {
            jQuery('#progressModal').modal('hide');
            common.showMessage(ev.error.message,false);
        });
        xhr.addEventListener("abort", function(ev) {
            jQuery('#progressModal').modal('hide');
            common.showMessage(ev.error.message,false);
        });
        xhr.send();
    });
});

通过响应的头信息获取下载的文件格式和文件名,这里我们下载的是Excel表格,根据需要更改。

处理响应内容代码

function processRequest(xhr){
    if (xhr.status == 200) { 
        var response = xhr.response;  
        var contentType = xhr.getResponseHeader("Content-Type");
        if(contentType.split(";")[0] == "application/json"){
            var reader = new FileReader();
            reader.readAsText(response);
            reader.onload = function (oFREvent) {
                common.showMessage(JSON.parse(reader.result).error.message,false);
            };
        } else if (contentType.split(";")[0] == "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"){
            var fileName =  xhr.getResponseHeader("content-disposition").split("UTF-8''")[1];
            saveFile(response, decodeURI(fileName))
        }
    }else{
        jQuery('#progressModal').modal('hide');
        var response = xhr.response;  
        var contentType = xhr.getResponseHeader("Content-Type")
        if(contentType.split(";")[0] == "application/json"){
            var reader = new FileReader();
            reader.readAsText(response);
            reader.onload = function (oFREvent) {
                common.showMessage(JSON.parse(reader.result).error.message,false);
            };
        }
    }
}

拿到文件格式和文件内容后,我们需要根据浏览器来实现文件的保存,这个部分花了很多时间研究。因为各个浏览器不同,因此我们需要根据不同的浏览器实现文件的保存。以下代码在Firefox,Chrome,IE和Edge中测试成功。

文件保存代码

function saveFile(blob, fileName){
    var b = getBrowser();
    if(b =="Chrome"){
        var link = document.createElement('a');
        var file = new Blob([blob], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
        link.href = window.URL.createObjectURL(file);
        link.download = fileName;
        link.click(); 
    } else if(b =="Firefox"){
        var file = new File([blob], fileName, { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
        var url = URL.createObjectURL(file);
        //window.location.href = url;
        parent.location.href = url;
    } else if(b=="IE"){
        var file = new Blob([blob], { type: 'application/force-download' });
        window.navigator.msSaveBlob(file, fileName);
    }
}

判断浏览器类型

function getBrowser() {  
    var ua = window.navigator.userAgent;  
    //var isIE = window.ActiveXObject != undefined && ua.indexOf("MSIE") != -1;  
    var isIE = !!window.ActiveXObject || "ActiveXObject" in window;
    var isFirefox = ua.indexOf("Firefox") != -1;  
    var isOpera = window.opr != undefined;  
    var isChrome = ua.indexOf("Chrome") && window.chrome;  
    var isSafari = ua.indexOf("Safari") != -1 && ua.indexOf("Version") != -1;  
    if (isIE) {  
        return "IE";  
    } else if (isFirefox) {  
        return "Firefox";  
    } else if (isOpera) {  
        return "Opera";  
    } else if (isChrome) {  
        return "Chrome";  
    } else if (isSafari) {  
        return "Safari";  
    } else {  
        return "Unkown";  
    }  
} 

到此XMLHttpRequest异步下载前台代码已全部实现。
项目用的是Java Web,框架是Spring MVC和Mybatis。

后台代码

@RequestMapping(PageUrlConstants.VP_GUI_0008_SCRATCH_EXPORT)
    public void downloadFile(HttpServletRequest request, HttpServletResponse response) throws Exception {
        init(request);
        String vnfTaskId = request.getParameter(PARAM_VNF_TASK_ID);
        String definitionBodyId = request.getParameter(PARAM_DEFINITION_BODY_ID);
        OutputStream out = null;
        // 1) 定義体・コマンドテーブルを検索する。
        Map result = definitionBodyDetailInputService.downloadDefinitionBodyDetailInput(vnfTaskId, definitionBodyId);
        // 2) 処理1)で取得した定義体・コマンド.データ"をxmlファイルに出力する。
        String definitionBodyName = "";
        String data = "";
        if (null != result) {
            definitionBodyName = (String) result.get(KEY_DEFINITION_BODY_NAME);
            data = (String) result.get(KEY_DATA);
        }

        // ファイル名: <1)で取得した 定義体マスタ.定義体名>_<YYYYMMDDhhmmss>.xml
        //String dateFormat = "YYYYMMDDhhmmss";
        //artf212696
        DateFormat df = new SimpleDateFormat(DatetimeConstants.DATE_FORMAT_STYLE_C);
        Date currentDate = new Date();
        String dateString = df.format(currentDate);
        String fileName = definitionBodyName + "_" + dateString + ".xml";
        if (null == data) {
            data = " ";
        }
        // 3) ファイルを、Windows端末にダウンロードするためのダウンロードダイアログを表示する。
        InputStream ins = new ByteArrayInputStream(data.getBytes());
        //artf214128
        String userAgent = request.getHeader("User-Agent");
        byte[] bytes = userAgent.contains("MSIE") ? fileName.getBytes() : fileName.getBytes("UTF-8");
        fileName = new String(bytes, "ISO-8859-1"); 
        response.setHeader("Content-disposition",String.format("attachment; filename=\"%s\"", fileName));
        //response.setHeader("content-disposition", "attachment;filename=" + URLEncoder.encode(fileName, ENCODE_UTF_8));
        out = response.getOutputStream();
        byte[] b = new byte[1024];
        int len = -1;
        while ((len = ins.read(b)) != -1) {
            out.write(b, 0, len);
        }
        out.flush();
        out.close();
        ins.close();
    }

你可能感兴趣的:(XMLHttpRequest实现异步下载)