寫這個博客之前我並不清楚 ajax請求是下載不了文件的 😅 這段時間在寫一個自己的項目,用到了ajax下載文件,請求到了controller層並返回文件下載成功 但是瀏覽器就是沒有反應,找了很多資料以及在網上搜了很多文章,但找到的文章的着重點 還是在controller層對文件的處理上,最后為了驗證controller是沒有問題的,我就在jsp中直接寫了一個form表單提交,可想而知的效果是文件順利下載下來了,所以反過來想應該是ajax請求的問題,因為我的業務場景是 當用戶點擊下載按鈕時,控制層接收到請求后 先去查詢數據,然后拼裝成一個Excel文件,把文件上傳到服務器,我的想法是由於中間的流程或者說邏輯有些復雜可能會出現一些不可預料的異常 需要反饋給用戶,所以這就變成了不僅是下載文件這么簡單,如果直接用form表單提交 我就無法展示系統的錯誤信息。
這中間也發生了這樣的異常 java.lang.IllegalStateException: getOutputStream() has already been called for this response;
遇到這樣報錯的原因是:controller層返回了一個jsp,但是這個請求是 下載文件
返回一個頁面時web容器生成的servlet代碼中有out.write("") 字符型輸出流,但是在寫文件到瀏覽器時 我用response.getOutputStream() 字節型輸出流,所以導致了這個沖突
其實這個異常和本次我寫這個博客的目的無關 只是我在解決這個業務場景時 原本想到的解決方案但出現了異常也就pass(淘汰)了我這個想法,所以為了讓自己以后不會出現相同的問題就隨口一說
應對這個場景正確的解決方案:
前端jsp://一個輸入框 兩個按鈕 一個按鈕是計算 一個按鈕是下載
<label>請輸入一個標識符:</label>
<div>
<input id="flagnum" type="text" name="flagNum" />
</div>
<button id="button-cal">calculation</button>
<button id="button-down">download</button>
js:// 計算的忽略 只看下載
$(function(){
// 監聽download按鈕的點擊事件 相當於document.getElementById("button-down")
$("#button-down").click(function(){
// 獲取兩個標簽之間的值用 .text(); 標簽內的value值 用 .val();
var flagNum = $("#flagnum").val();
//發送ajax請求
$.ajax({
url: "export",
type: "post",
data: {"flagNum": flagNum},
success: function(data){
//這一塊我在后端封裝了一個響應對象 {message:{success:true, returnObject{這里是一個map}, msg:"響應信息", code:響應狀態碼, name:響應名稱},data:{這里是一個map}}
var message = data.message;
if(message.success){
//調用如下用於拼接form表單請求的download方法
// url 是請求的路徑 name就是input表單中的name value是export這個請求獲取的服務器中的文件地址 method 是請求方法 post / get
download("url","name","value","method");
}else {
alert(這里輸出一下錯誤信息);
}
},
error: funciton(e){
alert(接口調用失敗);
}
});
});
});
function download(url,name,value,method){
//拼接一個form表單 里面有input標簽 追加到<body>標簽中 提交后刪除這個form表單;
$(<form action="'+url+'" method="'+method+'"><input type="text" name="'+name+'" value="'+value+'" /></form>).appendTo("body").submit().remove();
}
controller 層:
@RequestMapper("download")
public void download(HttpServletResponse response, @Param("filePath") String filePath){
FileDowloadUtil.windowDownload(response, filePath);
}
文件下載的工具類 FileDowloadUtil:
public static void windowDownload(HttpServletResponse response, String filePath){
// 判斷文件地址是否為空
if(filePath.isEmpty()){
return;
}
//獲取文件名
//String fileName = filePath.subString(filePath.lastIndexOf(".") +1);
File file = new File(filePath);
if(file == null){
return;
}
String fileName = file.getName();
//設置響應頭,控制瀏覽器下載該文件 設置下載的文件為excel application/vnd.ms-excel 下載別的文件是 百度下 content-type 對應的文件類型即可
try {
fileName = URLEncoder.encode(fileName,"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
response.setHeader("content-type","application/vnd.ms-excel");
response.setContentType("application/octet-stream");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
byte[] bytes = new byte[1024];
InputStream is = null;
OutputStream out = null;
try {
is = new FileInputStream(file);
out = response.getOutputStream();
int len = 0;
while ((len = is.read(bytes)) != -1){
out.write(bytes,0,len);
out.flush();
}
} catch (UnsupportedEncodingException e) {
log.error("異常:"+e.getMessage());
} catch (IOException e){
log.error("異常:"+e.getMessage());
} finally {
if (out != null){
try {
out.close();
} catch (IOException e) {
log.error("異常:"+e.getMessage());
}
}
if (is != null){
try {
is.close();
} catch (IOException e) {
log.error("異常:"+e.getMessage());
}
}
}
}
如上就是解決方案的全部代碼;