day12-SpringMVC檔案上傳

一刀一個小西瓜發表於2023-02-17

SpringMVC檔案上傳

1.基本介紹

  1. SpringMVC 為檔案上傳提供了直接的支援,這種支援是透過即插即用的 MultipartResolver 實現的。spring 用 Jacarta Commons FileUpload 技術實現了一個 MultipartResolver 的實現類:CommonsMultipartResovler

  2. SpringMVC 上下文預設沒有裝配 MultipartResolver ,因此預設情況下不能處理檔案的上傳工作。如果要使用 Spring 檔案上傳功能,要先在上下文(容器檔案)中配置 MultipartResolver

    <!--配置檔案上傳解析器-->
    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" 
          id="multipartResolver">
    </bean>
    

2.應用例項

(1)引入 SpringMVC 檔案上傳需要的 jar 包

image-20230217200556207

(2)建立 fileUpload.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>檔案上傳</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/fileUpload"
      method="post" enctype="multipart/form-data">
    檔案介紹:<input type="text" name="introduce"/><br/>
    選擇檔案:<input type="file" name="file"/><br/>
    <input type="submit" value="上傳檔案"/>
</form>
</body>
</html>

(3)web.xml 檔案中配置過濾器,處理中文亂碼問題(這裡使用 spring 提供的過濾器)

<!--使用spring提供的過濾器處理中文-->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <!--大小寫都支援-->
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

(4)MyCharacterFilter.java

package com.li.web.filter;

import javax.servlet.*;
import java.io.IOException;

/**
 * @author 李
 * @version 1.0
 * 編寫過濾器處理中文亂碼問題
 */
public class MyCharacterFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest servletRequest, 
                         ServletResponse servletResponse, 
                         FilterChain filterChain) throws IOException, ServletException {
        //加入對編碼的處理
        servletRequest.setCharacterEncoding("utf-8");
        //放行請求
        filterChain.doFilter(servletRequest, servletResponse);
    }

    @Override
    public void destroy() {}
}

(5)在 spring 的容器檔案中,配置檔案上傳解析器

<!--配置檔案上傳解析器-->
<!--這裡是按照介面名字獲取bean,因此 id要寫為 multipartResolver(介面)-->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
      id="multipartResolver">
</bean>

(6)建立 FileUploadHandler.java

package com.li.web.fileupload;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;

/**
 * @author 李
 * @version 1.0
 * 處理檔案上傳的 handler
 */
@Controller
public class FileUploadHandler {
    //編寫方法,處理檔案上傳的請求
    @RequestMapping(value = "/fileUpload")
    public String fileUpload(@RequestParam(value = "file") MultipartFile multipartFile,
                             HttpServletRequest request, String introduce) 
        throws IOException {
        //接收到提交的檔名
        String originalFilename = multipartFile.getOriginalFilename();
        System.out.println("你上傳的檔案介紹=" + introduce);
        System.out.println("你上傳的檔名=" + originalFilename);
        //得到上傳的檔案要儲存的路徑[全路徑:包括檔名]
        //這裡其實是out目錄下的路徑
        String fileFullPath =
                request.getServletContext().getRealPath("/img/" + originalFilename);
        //建立檔案
        File saveToFile = new File(fileFullPath);
        //將上傳檔案轉存到 saveToFile
        multipartFile.transferTo(saveToFile);
        return "success";
    }
}

(7)啟動 tomcat,訪問 fileUpload.jsp,選擇檔案上傳

image-20230217204739917

後臺輸出:

image-20230217205540158

上傳的檔案:

這裡只是模擬,真正開發中不會將檔案存放在專案目錄中,而是存放在主機的另外的磁碟上,每次存放檔案時會將存放路徑儲存到資料庫中。下載檔案時會讀取資料庫資料,得到路徑,尋找檔案。

image-20230217204906903

postman 也可以進行檔案上傳:

image-20230217220956037

3.transferTO()方法

@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
    //首先判斷該檔案(路徑)在磁碟中是不是可獲取的
   if (!isAvailable()) {
      throw new IllegalStateException("File has already been moved - cannot be transferred again");
   }

    //判斷上傳的檔案在磁碟中存不存在,如果存在就會去嘗試刪除該檔案,如果刪除失敗,就會丟擲異常
   if (dest.exists() && !dest.delete()) {
      throw new IOException(
            "Destination file [" + dest.getAbsolutePath() + "] already exists and could not be deleted");
   }

    //如果檔案不存在,或者存在但刪除成功了
   try {
       //將上傳的檔案複製到目標目錄 dest 下
       //this.fileItem 就是你要上傳檔案的臨時檔案(tmp檔案)
       //就是說 SpringMVC 上傳檔案也是先將上傳檔案儲存為臨時檔案,然後再複製為目標檔案
      this.fileItem.write(dest);
      LogFormatUtils.traceDebug(logger, traceOn -> {
         String action = "transferred";
         if (!this.fileItem.isInMemory()) {
            action = (isAvailable() ? "copied" : "moved");
         }
         return "Part '" + getName() + "',  filename '" + getOriginalFilename() + "'" +
               (traceOn ? ", stored " + getStorageDescription() : "") +
               ": " + action + " to [" + dest.getAbsolutePath() + "]";
      });
   }
   catch (FileUploadException ex) {
      throw new IllegalStateException(ex.getMessage(), ex);
   }
   catch (IllegalStateException | IOException ex) {
      // Pass through IllegalStateException when coming from FileItem directly,
      // or propagate an exception from I/O operations within FileItem.write
      throw ex;
   }
   catch (Exception ex) {
      throw new IOException("File transfer failed", ex);
   }
}