javascript
JavaScript————FormData实现多文件上传
引言
星期四的時候,我遇到了一個文件上傳的問題,與以往不同的是,這一次上傳的是多個文件,而且涉及到了久違的javascript代碼。
雖然最后實現的并不盡如人意,不過也算是完成了功能,接下來就把我發現的一些問題和總結記錄一下。
HTML部分展示
<form id ="tempFile" method="post" enctype="multipart/form-data"><!-- 指令模板文件上傳 --><div style="text-align: center;"><div class="clearfix" style="width:400px;margin:0 auto;"><input id ="templatesFile" type="file" multiple="multiple" /></div></div> </form>上述代碼可以看到,只是一個簡單的 form表單,里面嵌套了一個 input 輸入框,這個 input 輸入框的 type 類型是file,值得注意的是,如果是允許上傳多個文件,那么input輸入框必須添加multiple="multiple" 屬性。
這里要說明一點,由于使用 submit 按鈕提交 form表單會造成頁面刷新,因此,現在一般都采用按鈕與 form 表單分離的方式,使用 AJAX來異步上傳數據。 如下所示:
JS代碼展示
/*** 上傳模板文件* @returns*/ function sendTemplates() {// 需要上傳的文件 // var templates = document.getElementById("templatesFile").files;var templates = $("#templatesFile")[0].files;if (templates.length > 2) {alert("上傳失敗,一次最多兩個文件!");return;}var formdata = new FormData();for (var i = 0 ; i < templates.length ; i++) {formdata.append("temp" + i, templates[i]);}$.ajax({url : "/manager/uploadTemplates",type : "POST",data : formdata,processData : false,contentType : false,success : function(resultMap) {if (resultMap.code == "OK") {alert("上傳成功!");} else if (resultMap.code == "FAIL") {alert(resultMap.msg);}},error : function(e) {alert("服務器異常");}}); }代碼已經保留了關鍵的代碼部分,剔除了一些無關緊要的操作。
Java代碼實現
@RequestMapping(value = "/uploadTemplates", method = RequestMethod.POST) @ResponseBody public Map<String, Object> uploadTemplates(HttpServletRequest request) {MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;List<MultipartFile> templatesList = new ArrayList<>();for (int i = 0 ; i < 2 ; i++) {templatesList.addAll(multipartRequest.getFiles("temp" + i));}MultipartFile[] templates = new MultipartFile[templatesList.size()];templatesList.toArray(templates);Map<String, Object> result = templateService.uploadTemplates(templates);return result; }看到上面的代碼,我不得不說,真的和 shi一樣。不過目前我還沒找到很好的辦法。
好在 for 循環的 getFiles(...) 如果取不到值的話,就會返回一個空的 List 不會直接拋出異常。
以上就是所有關鍵的代碼部分,下面來說一說調試過程中我使用過的幾種方法,以及覺得很坑的地方。
第一坑:jQuery取files要加個[0]
回看js 部分的代碼,相信你已經注意到了:
var templates = $("#templatesFile")[0].files;這是通過jQuery選擇器找到 id="templatesFile" 的 input標簽,并獲取其中存儲的 FileList
而FileList 并不是一個數組。如果希望將它轉化成一個數組,可以使用 Array.from(FileList)
不過不論是 FileList還是數組,都不是重點,重點是$("#templatesFile")[0] 一定要加上后面的這個 [0] 。而如果使用document.getElementById("...")就不必加 [0]。 這是因為 files 屬性是原生js屬性,而不是jQuery屬性,因此需要通過這個 [0] 將jQuery對象轉化為 js對象,才能通過 .files 獲得這個FileList !
第二坑:$.ajax 的data域要怎么寫以及controller如何接收
說實話,前面的 js 與 java部分的代碼實在是迫不得已,不過依然沒有找到恰當合適的方法。
這個問題困擾了我整整一天的時間,從星期四的下午開始研究一直到第二天星期五的上午才勉強以上面的代碼跑通功能。
之前做過上傳單個文件的功能,比如這一篇《Java實現用戶頭像上傳》。那天我樂觀的以為,只要前端能夠獲取到 files 數組,然后后端的接口使用 MultipartFile[] 來接收就可以大功告成了!像這樣:
但是前端的代碼我試過N種方法依然無法成功接收到這個文件數組!不論是這樣:
還是這樣:
亦或是將數組轉化成String,然后后臺用String接收:
都 完!全!沒!用!
明明以前傳其他類型的參數是OK的啊!這到底是為什么?
于是看到了 FormData 的解決方案。但是事情依然并不簡單。
一開始我的思路是封裝一個FormData對象,然后把文件數組放入到一個value中,并且指定一個key ,這樣后臺通過 getFiles() 方法就可以獲得整個文件列表了。但是并沒有奏效。
在網上找了下原因,看到了一些別人的代碼,**全都是每一個key放一個文件!**原因是:
原文鏈接:Form?Data 對象的使用
簡單的說, String、Blob、File可以被FormData傳輸,如果是其他類型,則會被轉化成字符串,然而,對于FileList又會是怎樣的情況呢?我找到了下面這句話:
Using the FormData API is the simplest and fastest, but has the
disadvantage that data collected can not be stringified.
翻譯過來就是,使用FormData最簡單,也最高效,但是有一個缺點是,數據集合(比如List或者數組)無法被序列化為字符串。
我真的崩潰了!也就是說,我必須將文件列表(或者數組)中的每一個文件取出來,分別給每一個文件指定一個Key才能成功的通過formdata傳輸到后臺并接收。
所以才有了這樣的代碼:
和這樣的代碼:
public Map<String, Object> uploadTemplates(HttpServletRequest request) {MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;List<MultipartFile> templatesList = new ArrayList<>();for (int i = 0 ; i < 2 ; i++) {templatesList.addAll(multipartRequest.getFiles("temp" + i));}// some other codes... }
到目前為止,還沒有找到更加簡潔和高效的代碼,有哪位全棧的哥們可以告訴我嗎?
歡迎評論區留言哦!非常感謝!
總結
以上是生活随笔為你收集整理的JavaScript————FormData实现多文件上传的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: postman新手使用教程
- 下一篇: Spring Boot————AOP入门