SpringMVC之文件上傳和下載

SpringMVC實現文件的上傳和下載 相對於JavaWeb階段咱們使用過servlet實現文件上傳和下載操做;而SpringMVC實現了對上傳操做的直接支持,提供了multipart解析器。MultipartFile提供了一些對文件操做的方法,使得文件上傳變得更簡單。不管上傳仍是下載都是進行二進制流的轉換,下面咱們以案例的形式瞭解一下如何使用SpringMVC實現文件的上傳操做。html

<!--more-->java

文件上傳

準備

瞭解

  1. 文件上傳咱們首先要考慮的就是把文件上傳到哪裏?是上傳到工程目錄下,仍是上傳到本地磁盤中?
  2. 由於上傳的文件通常都是二進制文件,因此咱們須要經過某種方式對錶單提交進行編碼。經過將enctype設置爲multipart/form-data,每一個輸入域都將做爲POST請求的不一樣部分進行提交(默認提交的表單中數據存儲格式是名字-值,顯然是不適合相似文件上傳那種二進制數據的)。
  3. Spring提供了對multipart數據的解析器CommonsMultipartResolverMultipartResolver接口的實現類),可是這個解析器是基於Apache Commons FileUpload技術的,因此須要commons-filrUpload.jar支持。

配置

除了咱們以前使用的Spring以及SpringMVC的先關jar依賴包,還須要導入如下jar文件:git

commons-fileupload.jar
commons-io.jar

實例

首先咱們看一下項目結構:github

注意:這裏我使用的配置是:IDEA + tomcat + Maven,對於maven項目咱們要清楚項目編譯後的文件都放在target目錄下。web

  1. 修改咱們的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

  1. 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方法:數據庫

  1. request.getSession().getServletContext().getRealPath(String s):獲取本項目下指定目錄的絕對路徑。
  2. MultipartFile.getOriginalFilename():獲取被MultipartFile綁定的上傳參數的原始名稱
  3. System.currentTimeMillis():獲取自1970年1月1日0時起到如今時間的毫秒數。
  4. MultipartFile.transferTo(): 在指定的磁盤路徑下生成一個新的文件。
  • jsp中的form表單將數據提交到這個映射方法,那麼咱們就要接受jsp中傳遞的參數,要注意的就是綁定image參數是MultipartFile數據類型。
  • 下面就要定義一個上傳文件的保存目錄,本例中使用request.getSession().getServletContext().getRealPath()獲取到的是本項目的絕對路徑,而getRealPath("resources/upload")是針對此項目的相對路徑,如此時的文件保存路徑實際上是:/Users/ty/Documents/Java/Learn/ssm/target/TyCoding/resources/upload
  • 若是指定路徑下的文件夾存在,就將上傳的文件寫入進這個文件夾中,若是此文件夾不存在,就調用mkdir()方法建立此文件夾。
  • 上述一切都準備就緒,最後會調用Service層的save()方法將表單數據保存到數據庫中。(**注意:**在調用的save()方法中咱們會看到實際寫入進數據庫中的上傳文件的路徑其實僅是相對此項目的相對路徑(即相對於webapp)。
  1. 最後咱們要在Spring配置文件中註冊這個multipart解析器:
<!-- 配置支持文件上傳 -->
<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);
    }
}

最後數據保存成功,跳轉到成功頁面: 看一下此文件在工程中的位置: 看一下儲存到數據庫中數據: 服務器

  1. 最後咱們看一下經過SQL語句: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>

答疑:

  1. 咱們設置的getRealPath("resources/upload")不應是相對於工程的路徑嗎?(也就是在webapp下)。爲何上傳的文件會保存到target目錄下? 答:咱們要明白Tomcat下運行的項目實際是編譯後的class文件,即此maven項目中的target文件夾中的數據。那麼咱們設置的文件上傳路徑實際上是在此編譯後的文件夾中的相對路徑(TyCoding是我設置的artifact id
  2. 爲何咱們保存到數據中的路徑是項目的相對路徑? 答:要知道在Tomcat下運行項目,咱們訪問的圖片路徑是須要在localhost:8080下的路徑,也就是當前項目的相對路徑,因此咱們若是保存一個帶盤符的絕對路徑是確定不能訪問到的。

文件下載

準備

瞭解

文件上傳咱們這裏設計的思路是:在頁面設置一個可點擊的鏈接(好比<a>),點擊便可下載,而咱們須要在其href屬性中拼接要下載的文件的名稱,而後經過這個請求路徑,Controller層的映射方法接收到你要下載的文件名稱,而後根據指定的下載文件的路徑查詢到這個文件的名稱,而後將文件轉換成二進制流,而後讓客戶端讀取這個二進制流寫入到本機中從而實現下載。

  1. ResponseEntity<byte[]>: SpringMVC提供的用於實現響應頭、文件數據(以字節儲存)、狀態封裝都一塊兒返回給瀏覽器實現文件的下載。
  2. header.setContentDispositionFormData(): 告訴瀏覽器要以指定的數據類型打開這個文件
  3. FileUtils.readFileToByteArray(): 使用FileUtils工具類強制將指定文件數據轉換成byte字節流的形式。

實例

  1. 首先咱們更改上面的save.jsp頁面,新增一個下載的鏈接:
<h2>文件下載</h2>
<a href="<%=basePath%>/user/download?fileName=圖片.jpg">點擊我下載圖片</a>

如上所示,咱們在請求路徑中拼接了fileName參數值是圖片.jpg那麼後臺接收到這個參數值,就會查詢指定位置的文件。

  1. 而後看一下Controller的請求
//文件下載
@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媒體格式類型,最後使用FileUtilsreadFileToByteArray將文件數據轉換成二進制字節流,連同設置好的響應數據格式一同返回給瀏覽器,實現文件的下載。

最後看一下實際的效果:

<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.

相關文章
相關標籤/搜索