SpringMVC實現文件的上傳和下載 相對於JavaWeb階段咱們使用過servlet實現文件上傳和下載操做;而SpringMVC實現了對上傳操做的直接支持,提供了multipart
解析器。MultipartFile
提供了一些對文件操做的方法,使得文件上傳變得更簡單。不管上傳仍是下載都是進行二進制流的轉換,下面咱們以案例的形式瞭解一下如何使用SpringMVC實現文件的上傳操做。html
<!--more-->java
enctype
設置爲multipart/form-data
,每一個輸入域都將做爲POST請求的不一樣部分進行提交(默認提交的表單中數據存儲格式是名字-值
,顯然是不適合相似文件上傳那種二進制數據的)。multipart
數據的解析器CommonsMultipartResolver
(MultipartResolver
接口的實現類),可是這個解析器是基於Apache Commons FileUpload
技術的,因此須要commons-filrUpload.jar
支持。除了咱們以前使用的Spring以及SpringMVC的先關jar依賴包,還須要導入如下jar文件:git
commons-fileupload.jar commons-io.jar
首先咱們看一下項目結構:github
注意:這裏我使用的配置是:IDEA + tomcat + Maven,對於maven項目咱們要清楚項目編譯後的文件都放在target
目錄下。web
save.jsp
<%@ page isELIgnored="false" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <% String path = request.getContextPath(); String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path; %> <html> <head> <title>Title</title> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> </head> <body> <h2>表單</h2> <form action="<%=basePath%>/user/save" method="post" enctype="multipart/form-data"> username: <input type="text" name="username"/><br/> password: <input type="password" name="password"/><br/> profile image:<input type="file" name="image"/><br/> <input type="submit" value="提交"/> </form> </body> </html>
解釋: 注意在上面<%%>
中的Java代碼是獲取本項目的相對路徑,至關於<%=pageContext.request.ContextPath%>
。接下來咱們將表單<form>
標籤中設置屬性enctype="multipart/form-data"
。spring
Controller
層//保存用戶 @RequestMapping(value = "/save", method = RequestMethod.POST) public String save(@RequestParam String username, @RequestParam String password, @RequestParam(value="image",required = false)MultipartFile image, HttpServletRequest request, Model model) { //獲取文件在服務器上的儲存位置 String path = request.getSession().getServletContext().getRealPath("resources/upload"); File filePath = new File(path); System.out.println("文件保存路徑:" + path); if (!filePath.exists() && !filePath.isDirectory()) { System.out.println("目錄不存在,建立目錄:" + filePath); filePath.mkdir(); } //獲取原始文件名稱 String originalFileName = image.getOriginalFilename(); System.out.println("原始文件名稱:" + originalFileName); //獲取文件類型,以最後一個`.`做爲標識 String type = originalFileName.substring(originalFileName.lastIndexOf(".") + 1); System.out.println("文件類型:" + type); //設置文件新名字 String fileName = System.currentTimeMillis() + "." + type; System.out.println("文件新名稱:" + fileName); //在指定路徑建立一個文件 File targetFile = new File(path, fileName); //將文件保存到服務器指定位置 try { image.transferTo(targetFile); model.addAttribute("message", "保存數據成功"); userService.save(username,password,"resources/upload/" + fileName); return "view/success"; } catch (IOException e) { System.out.println("保存文件錯誤..."); e.printStackTrace(); } return "view/error"; }
Controller層就比較複雜了,由於對於本案例而言文件上傳操做的主要代碼都在Controller層,而Service和Dao層僅是執行保存操做。咱們首先了解一下文件操做的API方法:數據庫
- request.getSession().getServletContext().getRealPath(String s):獲取本項目下指定目錄的絕對路徑。
- MultipartFile.getOriginalFilename():獲取被
MultipartFile
綁定的上傳參數的原始名稱- System.currentTimeMillis():獲取自1970年1月1日0時起到如今時間的毫秒數。
- MultipartFile.transferTo(): 在指定的磁盤路徑下生成一個新的文件。
image
參數是MultipartFile
數據類型。request.getSession().getServletContext().getRealPath()
獲取到的是本項目的絕對路徑,而getRealPath("resources/upload")
是針對此項目的相對路徑,如此時的文件保存路徑實際上是:/Users/ty/Documents/Java/Learn/ssm/target/TyCoding/resources/upload
。mkdir()
方法建立此文件夾。save()
方法將表單數據保存到數據庫中。(**注意:**在調用的save()
方法中咱們會看到實際寫入進數據庫中的上傳文件的路徑其實僅是相對此項目的相對路徑(即相對於webapp
)。<!-- 配置支持文件上傳 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver" p:maxUploadSize="500000"/>
maxUploadSize
是設置上傳文件的大小,能夠更改。瀏覽器
下面咱們看一下實際效果: 點擊提交按鈕,訪問後臺映射方法:
能夠看到已經獲取到表中的數據,並準備調用Service層保存數據,那麼咱們看一下最後的Dao層將數據存入進數據庫的方法:tomcat
public class UserDaoImp extends JdbcDaoSupport implements UserDao { //保存數據 public void save(String username, String password, String image) { //使用Spring提供的JDBC模板能夠直接執行SQL語句 this.getJdbcTemplate().update("insert into user(id,username,password,image) values(?,?,?,?)",null,username,password,image); } }
最後數據保存成功,跳轉到成功頁面:
看一下此文件在工程中的位置:
看一下儲存到數據庫中數據:
服務器
select * from user
,將查詢到的數據回顯到頁面上: list.jsp
<table border="1"> <tr> <td>id</td> <td>username</td> <td>password</td> <td>avatar</td> </tr> <c:forEach items="${userList}" var="user"> <tr> <td>${user.id}</td> <td>${user.username}</td> <td>${user.password}</td> <td><img src="<%=basePath%>/${user.image}" width="100" height="80"></td> </tr> </c:forEach> </table>
答疑:
getRealPath("resources/upload")
不應是相對於工程的路徑嗎?(也就是在webapp
下)。爲何上傳的文件會保存到target
目錄下? 答:咱們要明白Tomcat下運行的項目實際是編譯後的class文件,即此maven項目中的target
文件夾中的數據。那麼咱們設置的文件上傳路徑實際上是在此編譯後的文件夾中的相對路徑(TyCoding
是我設置的artifact id
)localhost:8080
下的路徑,也就是當前項目的相對路徑,因此咱們若是保存一個帶盤符的絕對路徑是確定不能訪問到的。文件上傳咱們這裏設計的思路是:在頁面設置一個可點擊的鏈接(好比<a>
),點擊便可下載,而咱們須要在其href
屬性中拼接要下載的文件的名稱,而後經過這個請求路徑,Controller層的映射方法接收到你要下載的文件名稱,而後根據指定的下載文件的路徑查詢到這個文件的名稱,而後將文件轉換成二進制流,而後讓客戶端讀取這個二進制流寫入到本機中從而實現下載。
ResponseEntity<byte[]>
: SpringMVC提供的用於實現響應頭、文件數據(以字節儲存)、狀態封裝都一塊兒返回給瀏覽器實現文件的下載。header.setContentDispositionFormData()
: 告訴瀏覽器要以指定的數據類型打開這個文件FileUtils.readFileToByteArray()
: 使用FileUtils工具類強制將指定文件數據轉換成byte字節流的形式。save.jsp
頁面,新增一個下載的鏈接:<h2>文件下載</h2> <a href="<%=basePath%>/user/download?fileName=圖片.jpg">點擊我下載圖片</a>
如上所示,咱們在請求路徑中拼接了fileName
參數值是圖片.jpg
那麼後臺接收到這個參數值,就會查詢指定位置的文件。
//文件下載 @RequestMapping("/download") public ResponseEntity<byte[]> download(HttpServletRequest request, @RequestParam(value="fileName",required = false) String fileName) throws Exception { try{ //下載路徑 String path = request.getServletContext().getRealPath("/resources/upload/"); File file = new File(path + File.separator + fileName); HttpHeaders headers = new HttpHeaders(); //解決文件名中文亂碼問題 String downloadFileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1"); //告訴瀏覽器以"attachment"方式打開文件 headers.setContentDispositionFormData("attachment",downloadFileName); //設置請求頭的媒體格式類型爲 application/octet-stream(二進制流數據) headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); return new ResponseEntity<byte[]>(FileUtils.readFileToByteArray(file),headers,HttpStatus.CREATED); } catch(Exception e){ e.printStackTrace(); System.out.println("文件下載出錯..."); return null; } }
首先要指定映射方法的返回數據類型是ResponseEntity<byte[]>
,首先要規定一個下載文件的目錄路徑,須要下載文件時,就會中這個路徑中查詢須要下載的文件,若是找不到指定文件,那麼就進catch裏面。 File.separator
用來分隔同一個路徑字符串中的目錄,至關於/
。找到了指定路徑下的文件後,須要解決中文亂碼問題,而後告訴瀏覽器要以attachment
的方式打開這個文件。 最後這隻ContentType
媒體格式類型,最後使用FileUtils
的readFileToByteArray
將文件數據轉換成二進制字節流,連同設置好的響應數據格式一同返回給瀏覽器,實現文件的下載。
最後看一下實際的效果:
<br/>
若是你們有興趣,歡迎你們加入個人Java交流羣:671017003 ,一塊兒交流學習Java技術。博主目前一直在自學JAVA中,技術有限,若是能夠,會盡力給你們提供一些幫助,或是一些學習方法,固然羣裏的大佬都會積極給新手答疑的。因此,別猶豫,快來加入咱們吧!
<br/>
If you have some questions after you see this article, you can contact me or you can find some info by clicking these links.