原因:AJAX 使用字元流交換,瀏覽器不識別為檔案下載。同步 AJAX 也無效。
改用隱藏 iframe 作為表單 target,後端若驗證失敗以串流方式回傳錯誤訊息,前端在 iframe onload 解析處理,避免頁面跳轉。
Response OutputStream 已用於傳送 Excel 二進位資料,無法再傳回 JSON 旗標。
解法:後端匯出完成後寫入一個短效 Cookie(如 updtstatus=1),前端 setInterval 輪詢 document.cookie,偵測到即清除 Cookie、清除計時器、啟用按鈕。
<button id="detailEp" onclick="return exportCheck(true);">匯出</button>
<script>
let timer;
$("#myForm").submit(function() {
$("button[type=submit]", this).attr("disabled", "disabled");
timer = setInterval(checkExportDone, 1000);
});
function checkExportDone() {
if (getCookie("updtstatus") === "1") {
clearInterval(timer);
$("#detailEp").removeAttr("disabled");
deleteCookie("updtstatus");
}
}
function getCookie(name) { /* 解析 document.cookie 回傳值 */ }
function deleteCookie(name) { /* 設定過期時間刪除 */ }
</script>
public void buildExcelDocument(Map<String, Object> data, String fileName,
HttpServletRequest req, HttpServletResponse resp) throws Exception {
HSSFWorkbook wb = new HSSFWorkbook();
// ... 建立 Sheet、寫入標題、資料列(略) ...
// 設定下載標頭
String encodedName = ChineseUtil.encodeFilename(fileName + ".xls", req);
resp.setContentType("application/vnd.ms-excel;charset=UTF-8");
resp.setHeader("Content-Disposition", "attachment;filename=" + encodedName);
try (OutputStream os = resp.getOutputStream()) {
wb.write(os);
os.flush();
}
// 關鍵:寫入 Cookie 通知前端匯出完成
Cookie ck = new Cookie("updtstatus", "1");
ck.setMaxAge(3600);
ck.setPath("/");
resp.addCookie(ck);
}
fetch + blob + URL.createObjectURL() 純前端下載(後端回傳二進位)。location.href = /download?token=xxx。