XMLHttpRequest非同期ダウンロードを実現

8073 ワード

背景
プロジェクトは、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();
    }