1. 程式人生 > >【前端】利用ajax實現偽檔案非同步上傳下載

【前端】利用ajax實現偽檔案非同步上傳下載

利用ajax可以實現很酷的效果,在不重新整理頁面的情況下提交表單、修改資料狀態等等,可是如果表單裡還有input:file可就慘了,ajax不支援檔案的處理啊!

ajax是使用了瀏覽器內部的XmlHttpRequest物件來傳輸XML資料的。既然是Xml的資料傳輸,那麼傳輸的資料肯定是文字的,而檔案上傳則需要傳輸二進位制的資料,顯然用ajax是不可能的。

可是ajax這麼好用,大家也都習慣了這種開發和使用體驗,試想如果form表單包含檔案上傳,那提交表單要重新整理整個頁面,提交成功還好,提交失敗了表單資料可都沒有了,這顆真難受。有沒有什麼辦法能實現類似的邏輯呢?

請注意以下屬性:form:target、iframe、parrent.callback() ,我們利用這些小屬性,來完成偽·ajax上傳下載檔案吧。

<!-- 這是資料表單 -->
<form action="/upload" id="form1" method="post"  enctype="multipart/form-data" target="hidden_frame">
	<input type="file" name="uploadfile" accept="*" />
	<input type="submit" value="提交" />
</form>
<!-- 這是一個隱藏的iframe -->
<iframe name="hidden_frame" id="hidden_frame" style="display:none;"></iframe>
<script type="text/javascript">
// 這是接收回調的函式
function callback(status, message) {
	alert(message);
}
</script>

上邊是html的大致結構,form表單包含了input:file,target到一個隱藏的iframe,這個隱藏的iframe並沒有做任何事情,最後是一個接受回撥的函式叫callback。

接下來就是伺服器這邊的邏輯了。

/upload這個介面接收到請求之後,從request裡獲取上傳檔案,名字就是input的name,然後進行邏輯處理,最後返回html資料。

接下來重點來了:

response返回的時候需要設定ContentType:text/html;charset=UTF-8;並且把結果拼成js程式碼寫入返回的流中。

response.setContentType("text/html;charset=UTF-8");
// status表示處理結果的狀態碼,message為對應的提示資訊
response.getWriter().print("<script>parent.callback(status, message);</script>");

注意到返回的js片段,呼叫了parrent的callback函式。

這裡的結果會返回到我們之前的隱藏iframe中,parent就是html主頁面,callback就是我們提前寫好的回撥函式。

到這裡,關於檔案上傳的技巧就說完了,接下來講講關於檔案下載的問題。

關於檔案下載,我在工作當中遇到最多的場景就是在xx後臺,有一堆查詢條件,查詢結果按表格展示,然後另外有個單獨的按鈕叫做“匯出”,講講怎麼使匯出功能更加優雅。

首先,按了匯出按鈕之後server會進行對應的邏輯處理拿資料,之後把資料寫到返回裡,這中間一旦出現問題就只能返回錯誤的提示資訊了。

這個邏輯有沒有跟上邊上傳的情形很像呢? 我們利用類似的邏輯,來實現帶錯誤提示的檔案下載。

首先先上html程式碼片段

<!-- 查詢表單 -->
<form action="/search" method="post" id="searchForm">
	<input name="query1" type="input" />
	<input name="query2" type="input" />
	
	<input type="button" id="search" value="查詢" />
	<input type="button" id="export" value="匯出" />
</form>
<!-- 隱藏的iframe -->
<iframe name="hidden_frame" id="hidden_frame" style="display:none;"></iframe>

<script type="txt/javascript" src="jquery-1.11.3.min.js"></script>
<script type="text/javascript">
$('input#search').bind('click',function(){
	$('#searchForm').attr('action','');
	$('#searchForm').attr('target','');
	$('#searchForm').submit();
})
$('input#export').bind('click',function(){
	$('#searchForm').attr('action','/export');
	$('#searchForm').attr('target','hidden_frame');
	$('#searchForm').submit();
})

// 這是接收回調的函式
function callback(status, message) {
	alert(message);
}
</script>

程式碼分三部分,第一部分是查詢表單,有一些查詢引數,兩個按鈕分別是查詢和匯出。第二部分是一個隱藏的iframe,作用跟最上邊的例子一樣。第三部分是js程式碼,包含了3個方法,第一個定義查詢按鈕的行為-重置了form的target和action,第二個定義了匯出的target和action,最後是接收回調的函式

請求到了伺服器之後進行處理,查詢邏輯就走查詢的流程,把資料返回後展示到頁面;匯出的話有3種可能,有資料、沒有資料、出現異常,如果要把狀態都反饋到前端,那麼這麼做試試:

首先,異常和沒有資料的情況,都可以按上傳檔案的返回方式,把處理結果的狀態和資訊封裝到js片段,通過parent.callback返回前端。

response.setContentType("text/html;charset=UTF-8");
// status表示處理結果的狀態碼,message為對應的提示資訊
response.getWriter().print("<script>parent.callback(status, message);</script>");

如果有資料,那麼需要在伺服器端把資料寫入返回流,並且把response的contentType置為application/x-download,程式碼示意
response.setContentType("application/x-download");
// 描述,filename欄位可以設定匯出檔案的名字,注意URLEncode處理下才可以支援中文
response.setHeader("Content-Disposition","attachment;filename=exportfilename");
// status表示處理結果的狀態碼,message為對應的提示資訊
OutputStream stream = response.getOutputStream();
// infobytes是需要匯出的資料
byte[] infobytes = new byte[100]; 
stream.write(infobytes);
stream.flush();

這樣如果有資料,前端收到請求之後就會走瀏覽器的下載流程,如果沒有資料或者出現異常,可以在不重新整理頁面的情況下給出提示。

注:以上程式碼的後端邏輯是以java為基礎的虛擬碼,需要轉化下才能真正使用