1. 程式人生 > 實用技巧 >Spring Boot 處理檔案上傳及路徑回顯

Spring Boot 處理檔案上傳及路徑回顯

實驗介紹

1.1 實驗內容

檔案上傳是比較常見和被使用者熟知的功能模組,常用場景有頭像設定、產品預覽圖、報表檔案儲存等等,在這些場景中都需要使用到檔案上傳功能,本篇文章將會對檔案上傳的大致流程及功能設計進行詳細的介紹,並結合實踐案例講解如何使用 Spring Boot 實現檔案上傳及相關注意事項,對檔案上傳的整個流程進行閉環。

1.2 實驗知識點

  • Spring MVC 檔案上傳流程
  • Spring Boot 檔案上傳功能實現
  • Spring Boot 檔案上傳路徑回顯

1.3 實驗環境

  • JDK 1.8 或者更高版本
  • Spring Boot 2.1.0-RELEASE
  • Maven 3+

1.4 程式碼獲取

wget https://labfile.oss.aliyuncs.com/courses/1244/lou-springboot-08.zip
unzip lou-springboot-08.zip
cd lou-springboot

Spring MVC 檔案上傳流程

以往在使用 Spring 的 web 專案開發中,我們通常會使用 Spring MVC 框架提供的檔案上傳工具類進行檔案上傳,也就是 MultipartResolver ,利用 SpringMVC 實現檔案上傳功能,離不開對 MultipartResolver 的設定,MultipartResolver 這個類,你可以將其視為 SpringMVC 實現檔案上傳功能時的工具類,這個類也只會在檔案上傳中發揮作用,在配置了具體實現類之後,SpringMVC 中的 DispatcherServlet 在處理請求時會呼叫 MultipartResolver 中的方法判斷此請求是不是檔案上傳請求,如果是的話 DispatcherServlet 將呼叫 MultipartResolver 的 resolveMultipart(request) 方法對該請求物件進行裝飾並返回一個新的 MultipartHttpServletRequest 供後繼處理流程使用,注意!此時的請求物件會由 HttpServletRequest 型別轉換成 MultipartHttpServletRequest 型別,這個類中會包含所上傳的檔案物件可供後續流程直接使用而無需自行在程式碼中實現對檔案內容的讀取和物件封裝的邏輯。

在 Spring Boot 中也是通過該工具類進行檔案上傳,與普通的 Spring web 專案不同的是,Spring Boot 在自動配置 DispatcherServlet 時也配置好了 MultipartResolver ,而無需再像原來那樣在 springmvc 配置檔案中增加檔案上傳配置的 bean。

Spring Boot 檔案上傳功能實現

這一小節將會通過一個檔案上傳案例的實現來講解如何使用 Spring Boot 進行檔案上傳功能。

常用配置

由於 Spring Boot 自動配置機制的存在,我們並不需要進行多餘的設定,只要已經在 pom 檔案中引入了 web starter 模組即可直接進行檔案上傳功能,在前面的實驗中我們已經將 web 模組整合到專案中,因此無需再進行整合。雖然不用配置也可以使用檔案上傳,但是有些開發者可能會在檔案上傳時有一些特殊的需求,因此也需要對 Spring Boot 中 MultipartFile 的常用設定進行介紹,配置和預設值如下:

配置含義註釋:

  • spring.servlet.multipart.enabled
    是否支援 multipart 上傳檔案,預設支援

  • spring.servlet.multipart.file-size-threshold
    檔案大小閾值,當大於這個閾值時將寫入到磁碟,否則存在記憶體中,(預設值 0 ,一般情況下不用特意修改)

  • spring.servlet.multipart.location
    上傳檔案的臨時目錄

  • spring.servlet.multipart.max-file-size
    最大支援檔案大小,預設 1 M ,該值可適當的調整

  • spring.servlet.multipart.max-request-size=10Mb
    最大支援請求大小,預設 10 M

  • spring.servlet.multipart.resolve-lazily
    判斷是否要延遲解析檔案(相當於懶載入,一般情況下不用特意修改)

上傳功能實現

注意:專案統一建立在/home/project/lou-springboot 目錄下。

建立檔案上傳儲存目錄

mkdir /home/project/upload

新建檔案上傳頁面
在 static 目錄中新建 upload-test.html,上傳頁面程式碼如下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Spring Boot 檔案上傳測試</title>
  </head>
  <body>
    <form action="/upload" method="post" enctype="multipart/form-data">
      <input type="file" name="file" />
      <input type="submit" value="檔案上傳" />
    </form>
  </body>
</html>

這應該是大家都很熟悉的一個檔案上傳頁面 demo ,檔案上傳的請求地址為 /upload,請求方法為 post,需要注意的是在檔案上傳時要設定 enctype="multipart/form-data",頁面中包含一個檔案選擇框和一個提交框,如下所示:

新建檔案上傳處理 Controller
在 com.lou.springboot.controller 包下新建 UploadController,程式碼如下:

package com.lou.springboot.controller;

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

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

@Controller
public class UploadController {
    // 檔案儲存路徑  主要需要自己先建立對應upload資料夾路徑
    private final static String FILE_UPLOAD_PATH = "/home/project/upload/";
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ResponseBody
    public String upload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "上傳失敗";
        }
        String fileName = file.getOriginalFilename();
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //生成檔名稱通用方法
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
        Random r = new Random();
        StringBuilder tempName = new StringBuilder();
        tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(suffixName);
        String newFileName = tempName.toString();
        try {
            // 儲存檔案
            byte[] bytes = file.getBytes();
            Path path = Paths.get(FILE_UPLOAD_PATH + newFileName);
            Files.write(path, bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "上傳成功";
    }
}

由於已經自動配置了 MultipartFile ,因此能夠直接在控制器方法中使用 MultipartFile 讀取檔案資訊, @RequestParam 中的檔名稱需要與檔案上傳前端頁面設定的 name 屬性一致,如果檔案為空則返回上傳失敗,如果不為空則生成一個新的檔名,之後讀取檔案流並寫入到指定的上傳路徑中,最後返回上傳成功。

檔案上傳功能測試

重啟專案,開啟瀏覽器並輸入測試頁面地址https://93c7f0974014.simplelab.cn/upload-test.html,之後選擇需要上傳的檔案並進行點選上傳按鈕,之後可以等待後端業務處理了,如果看到上傳成功的提示,並且在 upload 目錄中看到儲存的新檔案檔案則表示功能實現成功,如果檔案較大可以適當調整配置項的值。



Spring Boot 檔案上傳路徑回顯

網上很多的 Spring Boot 檔案上傳教程,通常只講如何實現上傳功能,之後就不再繼續講解了,雖然也給了教程和程式碼,但是正常情況下,我們上傳檔案是要實際應用到業務中的,比如圖片上傳,上傳後我們需要知道它的路徑,最好能夠在頁面中直接看到它的回顯效果,像前一個步驟中,我們只是成功的完成了檔案上傳,但是如何去訪問這個檔案還不得而知。Spring Boot 不像普通的 web 專案可以上傳到 webapp 指定目錄中,通常的做法是使用自定義靜態資源對映目錄,以此來實現檔案上傳整個流程的閉環,比如前一小節中的實際案例,在檔案上傳到 upload 目錄後,增加一個自定義靜態資源對映,使得 upload 下的靜態資源可以通過該對映地址被訪問到,實現方法如下:

package com.lou.springboot.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SpringBootWebMvcConfigurer implements WebMvcConfigurer {
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/files/**").addResourceLocations("file:/home/project/upload/");
    }
}

通過該設定,所有以 /files/ 開頭的靜態資源請求都會對映到/home/project/upload/目錄下,

接下來修改UploadController

package com.lou.springboot.controller;

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

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;

@Controller
public class UploadController {

    private final static String FILE_UPLOAD_PATH = "/home/project/upload/";

    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ResponseBody
    public String upload(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return "上傳失敗";
        }
        String fileName = file.getOriginalFilename();
        String suffixName = fileName.substring(fileName.lastIndexOf("."));
        //生成檔名稱通用方法
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd_HHmmss");
        Random r = new Random();
        StringBuilder tempName = new StringBuilder();
        tempName.append(sdf.format(new Date())).append(r.nextInt(100)).append(suffixName);
        String newFileName = tempName.toString();
        try {
            // 儲存檔案
            byte[] bytes = file.getBytes();
            Path path = Paths.get(FILE_UPLOAD_PATH + newFileName);
            Files.write(path, bytes);

        } catch (IOException e) {
            e.printStackTrace();
        }
        return "上傳成功,圖片地址為:/files/" + newFileName;
    }
}

測試

接下來我們來測試一下上傳的檔案能否被訪問到,重啟專案並,在專案啟動成功後,可以點選頁面上方的 Web 服務直接在顯示檢視網站效果。

之後會在瀏覽器中彈出https://********.simplelab.cn 頁面,我們可以在瀏覽器中輸入如下地址開啟檔案上傳測試頁面:https://160d612088bc.simplelab.cn/upload-test.html,之後進行檔案上傳測試和路徑回顯測試,過程如下所示:

最後,需要確認一下 upload 目錄下是否存在該檔案,如下圖所示:

實驗總結

本篇文章首先對檔案上傳的流程及功能設計進行了介紹,之後結合實踐案例講解如何使用 Spring Boot 實現檔案上傳以及如何對已上傳的檔案進行路徑回顯,希望通過本實驗的講解,大家都能夠掌握如何使用 Spring Boot 進行檔案上傳,本文演示時都是使用的圖片檔案,同學們在練習時也可以使用其他格式的檔案進行測試,希望大家多多動手練習,以更快的掌握該知識點,後續在圖片管理模組實踐中我們會繼續對該功能進行拓展講解