spring上傳多個文件
① [FE] 用 FormData 上傳多個文件到 MultipartFile[] 介面
最近有一個場景,在提交表單的時候,需要實現添加附件的功能,
表單內容要先提交到服務端,創建一個 issue,然後再將附件添加到這個 issue 中。
所以,附件在用戶添加的時候,是 沒有立即上傳 的,
用戶可以隨意在瀏覽器端添加和刪除,issue 創建後再一起上傳。
前端採用的組件庫是 antd ,用到了 upload 組件。
服務端介面是自定義實現的,也許並不支持 antd upload 上傳組件的規范。
服務端接受數據時,使用了 MultipartFile ,這是 Spring 框架中常用的 寫法 。
我們先看看 html input[type=file] 組件默認行為,
點擊 「選擇文件」,瀏覽器會彈出一個窗口,
選中一個文件,點 「打開」,就會觸發 onchange 事件,
在 onchange 事件中,可以通過 e.target.files[0] 拿到剛才上傳的那個 File 對象 。
再來看一下 upload 組件的默認行為,
點擊 「添加」,瀏覽器也會彈出那個選擇文件的窗口,
選中一個文件,點 「打開」,發現上傳失敗了。
打開控制台,看到 upload 組件向 / 這個地址發送了一個 POST 請求,
數據格式如下,
我們可以向 upload 組件傳入 action 參數,修改 POST 請求地址,
但是,選中文件後立即上傳 不符合 我們的場景,我們需要提交表單之後,將多個文件統一上傳。
所以我們得自定義 upload 組件的行為。
upload 組件的有一個 customRequest 屬性( #api ),
它可以配置自定義的上傳行為。
我們的思路是,先將選中後自動上傳的行為取消掉,然後再在提交表單後統一上傳。
取消自動上傳 的實現片段如下,
我們只需要在 customRequest 回調中,調用它的 onSuccess 參數即可。
刪除也是可以用的,
現在我們添加兩個附件,
接著來看前端怎樣將這些附件,統一上傳給服務端,具體實現如下,
可以看到請求成功了(項目中的 url 跟本例稍有不同,下圖只為了示意),
還有幾個需要注意的點:
上文 httpClient.post 實際調用了 XMLHttpRequest 發送請求,可能會遇到 跨域 的問題。
所以在調試上傳介面的時候,需要檢查一下服務端的配置,是否支持跨域請求。
CORS 相關的內容大致如下:
在預檢請求階段,服務端對 OPTIONS 請求的響應頭中會包含 Access-Control-Allow-Origin ,
表明服務端接受該域 http://foo.example 的跨域請求。
註:
這里需要後端實現 OPTIONS 方法,後端框架一般會通過配置方式統一處理(返回 200 或 204,不能是 4xx)。
如果未配置統一處理方式,框架可能會直接返回 404 導致預檢請求失敗,CORS 請求也會失敗。
使用 XMLHttpRequest 發送請求時,也可以攜帶 cookie 信息,
同時 預檢請求中服務端響應頭,也要包含 Access-Control-Allow-Credentials ,否則就不會發送 cookie
對於附帶 cookie 的請求,伺服器不能設置 Access-Control-Allow-Origin 的值為 「 * 」,否則請求將會失敗。
而將 Access-Control-Allow-Origin 的值設置為具體的地址 http://foo.example ,請求才能成功。
我們上傳功能用到了攜帶 cookie 的跨域請求,
可以看到服務端響應頭中確實包含了, Access-Control-Allow-Credentials 和 Access-Control-Allow-Origin 兩個欄位。
Spring: Uploading Files
Spring: org.springframework.web.multipart #MultipartFile
ant-design v4.11.1
Ant Design - Upload #API
MDN: CORS
② 如何用SpringBoot框架來接收multipart/form-data文件
SpringBoot有它自己的接收請求的代碼。下面就給大家詳細介汪檔紹一下它是如何實現單個文件和多個文件上傳的功能的。
首選做一個簡單的案例,也就是單個文件上傳的案例。(這個案例是基於SpringBoot上面的,所以大家首先得搭建好SpringBoot這個框架)
前台HTML代碼:
[html] view plain
<html>
<body>
<formaction="/upload"method="POST"enctype="multipart/form-data">
<inputtype="file"name="file"/>
<inputtype="submit"value="Upload"/>
</form>
</body>
</html>
/**
*文件上傳具體實現方法;
*
*@paramfile
*@return
*/
@RequestMapping("/upload")
@ResponseBody
publicStringhandleFileUpload(@RequestParam("file")MultipartFilefile){
if(!file.isEmpty()){
try{
/*
*這段代碼執行完畢之後,圖片上傳到了工程的跟路徑;大家自己擴散下思維,如果我們想把圖片上傳到
*d:/files大家是否能實現呢?等等;
*這里只是簡單一個例子,請自行參考,融入到實際中可能需要大家自己做一些思考,比如:1、文件路徑;2、文件困粗亂名;
*3、文件格式;4、文件大小的限制;
*/
BufferedOutputStreamout=newBufferedOutputStream(
newFileOutputStream(newFile(
file.getOriginalFilename())));
System.out.println(file.getName());
out.write(file.getBytes());
out.flush();
out.close();
}catch(FileNotFoundExceptione){
e.printStackTrace();
return"上傳失敗,"+e.getMessage();
}catch(IOExceptione){
e.printStackTrace();
return"上傳失敗,"+e.getMessage();
}
return"上傳成功";
}else{
return"上傳失敗,因為文件是空的.";
}
}
<!DOCTYPEhtml>
<htmlxmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>HelloWorld!</title>
</head>
<body>
<formmethod="POST"enctype="multipart/form-data"action="/batch/upload">
<p>文件1:<inputtype="text"name="id"/></p>
<p>文件2:<inputtype="text"name="name"/></p>
<p>文件3:<inputtype="file"name="file"/></p>
<p><inputtype="submit"value="上傳"/></p>
</form>
</body>
</html>
@RequestMapping(value="/batch/upload",method=RequestMethod.POST)
@ResponseBody
publicStringhandleFileUpload(HttpServletRequestrequest){
=((MultipartHttpServletRequest)request);
List<凳隱MultipartFile>files=((MultipartHttpServletRequest)request)
.getFiles("file");
Stringname=params.getParameter("name");
System.out.println("name:"+name);
Stringid=params.getParameter("id");
System.out.println("id:"+id);
MultipartFilefile=null;
BufferedOutputStreamstream=null;
for(inti=0;i<files.size();++i){
file=files.get(i);
if(!file.isEmpty()){
try{
byte[]bytes=file.getBytes();
stream=newBufferedOutputStream(newFileOutputStream(
newFile(file.getOriginalFilename())));
stream.write(bytes);
stream.close();
}catch(Exceptione){
stream=null;
return"Youfailedtoupload"+i+"=>"
+e.getMessage();
}
}else{
return"Youfailedtoupload"+i
+"becausethefilewasempty.";
}
}
return"uploadsuccessful";
}
後台接收代碼:
[java] view plain
這樣就可以實現對multipart/form-data類型文件的接收了。那如果是多個文件外加多個欄位呢,下面接著看下一個多個文件上傳的案例。
前台HTML界面:
[html] view plain
後台接收代碼:
[java] view plain
這樣就可以實現對多個文件的接收了功能了。
③ springboot調整上傳文件大小限制
Spring Boot文件上傳,文件過大導致異常the request was rejected because its size (xxx) exceeds the configured maximum (xxx)
當進行文件操作時,如果文件體積過大,會拋出上述錯誤。解決方案有兩種,一種是通過復寫MultipartConfigElement;另一種是修改配置文件application.yml中multipart相關參數。
通過setMaxRequestSize限制限制上傳的多個文件的總大小,setMaxFileSize限制單個文件的最大值。
properties配置:
參考:
https://blog.csdn.net/gnail_oug/article/details/80324120
https://ld246.com/article/1592018534503
https://blog.csdn.net/qq_27886773/article/details/102695145
④ spring mvc 多文件上傳時報錯MultipartHttpServletRequest
1、確認<form name="contractForm" id="contractForm" action="" method="post" enctype ="multipart/form-data">;
2、確認commons-fileupload.jar 和 org.springframework.web.jar 存在環境
3、MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest)request;
這一句會報錯:ApplicationHttpRequest cannot be cast to MultipartHttpServletRequest
解決辦法是通過這種方式:
MultipartResolver resolver = new CommonsMultipartResolver(request.getSession().getServletContext());
MultipartHttpServletRequest multipartRequest = resolver.resolveMultipart(request);