背景
項目需要實現(xiàn)在下載Excel時,實時顯示文件下載的進度條鸿秆,不能直接調(diào)用瀏覽器下載牡拇。因此超鏈接下載,或者調(diào)用window.location.ref等方法無法滿足要求孵运,因此只有采用異步的方式秦陋。
為什么要用XMLHttpRequest
jQuery的AJAX是可以實現(xiàn)文件的異步上傳,但無法實現(xiàn)異步下載治笨,因此我們需要用到JS的XMLHttpRequest對象來實現(xiàn).
XMLHttpRequest實現(xiàn)異步下載驳概,我們需要將xhr的響應(yīng)類型設(shè)置為blog(responseType = "blob")赤嚼,在xhr的load事件中處理響應(yīng),并實現(xiàn)文件的保存顺又。
下載的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) {
// 結(jié)束下載事件:下載進度條的關(guān)閉
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();
});
});
通過響應(yīng)的頭信息獲取下載的文件格式和文件名更卒,這里我們下載的是Excel表格,根據(jù)需要更改稚照。
處理響應(yīng)內(nèi)容代碼
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);
};
}
}
}
拿到文件格式和文件內(nèi)容后蹂空,我們需要根據(jù)瀏覽器來實現(xiàn)文件的保存,這個部分花了很多時間研究果录。因為各個瀏覽器不同上枕,因此我們需要根據(jù)不同的瀏覽器實現(xiàn)文件的保存。以下代碼在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異步下載前臺代碼已全部實現(xiàn)斤彼。
項目用的是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<String, Object> 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();
}