SpringMvc 文件上傳開發總結


之前的博客介紹過 Servlet 的文件上傳和下載,對於文件下載來說,SpringMvc 跟 Servlet 的實現方式是一樣,這里就不再介紹了。對於文件上傳來說,雖然 SpringMvc 底層實現方式跟 Servlet 也是一樣的,都是使用第三方 commons-fileupload 的 jar 包組件來實現,但是 SpringMvc 隱藏了實現細節,簡化了代碼編寫量,大大提高了開發效率,使用起來非常方便。

本篇博客從實際開發場景出發,通過表單上傳和 ajax 異步上傳兩種代碼實現方式進行演示,在博客最后會提供源代碼下載。


一、搭建工程

新建一個 maven 項目,導入相關 jar 包,我所導入的 jar 包都是最新的,內容如下:

有關具體的 jar 包地址,可以在 https://mvnrepository.com 上進行查詢。

<dependencies>
    <!--導入 servlet 相關的 jar 包-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.2</version>
        <scope>provided</scope>
    </dependency>
    <!--導入 Spring 核心 jar 包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.3.18</version>
    </dependency>
    <!--導入 SpringMvc 的 jar 包-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.18</version>
    </dependency>
    <!--導入 jackson 相關的 jar 包-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.13.1</version>
    </dependency>
    <!--文件上傳組件支持-->
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
</dependencies>

<build>
    <!--設置插件-->
    <plugins>
        <!--具體的插件配置-->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <port>80</port>
                <path>/</path>
            </configuration>
        </plugin>
    </plugins>
</build>

需要注意的是:我們需要導入第三方的 commons-fileupload 文件上傳組件,SpringMvc 底層也是使用它來完成文件上傳的。

這次我們使用 tomcat 插件來運行網站進行測試,因為這樣可以直接在 IDEA 中看到實際運行測試的結果。

有關 tomcat 插件,可以到官網查詢,地址為:https://tomcat.apache.org/maven-plugin.html

目前 tomcat 官網的插件,最新版本也就是 tomcat7 的 2.2 版本。

配置好引用的 jar 包后,打開右側的 Maven 窗口,刷新一下,這樣 Maven 會自動下載所需的 jar 包文件。

搭建好的項目工程整體目錄比較簡單,具體如下圖所示:

image

com.jobs.config 包下存儲的是 SpringMvc 的配置文件和 Servlet 的初始化文件
com.jobs.controller 包下存儲的是用於提供 api 接口的類
com.jobs.exception包下存儲的是全局異常捕獲和處理類

web 目錄下放置的是網站文件,只有一個靜態頁面和一些 js 文件


二、SpringMvc 關鍵配置

有關 ServletInitConfig 和 SpringMvcConfig 的具體內容,跟之前發布的博客相比,重復性內容太多了,這里僅僅介紹關鍵的配置信息。在 SpringMvcConfig 類中增加 SpringMvc 裝載文件上傳組件 Bean 的獲取方法,具體內容為:

//配置 SpringMvc 上傳文件的解析器相關參數,底層依賴於第三方 commons-fileupload 的 jar 包組件
//需要注意的是:這里的上傳組件的 Bean 必須取名為 multipartResolver
@Bean("multipartResolver")
public CommonsMultipartResolver getMultipartResolver() {

    CommonsMultipartResolver cmr = new CommonsMultipartResolver();
    cmr.setDefaultEncoding("UTF-8");

    //設置單個文件大小限制(byte),這里設置為 4M
    cmr.setMaxUploadSizePerFile(4 * 1024 * 1024);

    //設置整個請求,最大允許上傳的文件總大小限制(byte),這里設置為 10M
    cmr.setMaxUploadSize(10 * 1024 * 1024);

    //設置上傳文件過程中,內存最多使用 10M
    cmr.setMaxInMemorySize(10 * 1024 * 1024);

    return cmr;
}

需要注意的是:Bean 的名稱必須為 multipartResolver ,否則文件無法上傳成功,具體原因可以查看 SpringMvc 的源碼。

另外我們還需要有一個全局的異常捕獲和處理的類,方便在文件上傳的過程中,如果出現問題,不至於顯示默認的報錯頁面。

package com.jobs.exception;

import org.springframework.stereotype.Component;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

@Component
@ControllerAdvice
public class ExceptionAdvice {

    //全局捕獲所有異常,並進行處理
    @ExceptionHandler(Exception.class)
    public String handException(Exception ex, Model m) {

        //將異常信息,記錄到日志中
        System.out.println("捕獲到的異常信息:" + ex);

        //給用戶展示友好的信息,隱藏具體的問題細節
        m.addAttribute("msg", ex.getMessage());
        return "fail";
    }
}

這里只編寫了一個異常處理方法,處理所有的異常。當然這里只是 demo 演示,當捕獲到異常時,跳轉到 fail.jsp 頁面展示錯誤信息。在實際開發場景中,你可以進行一些人性化的處理和信息提示。錯誤日志肯定是需要記錄的,方便排查和解決問題。


三、上傳文件的代碼細節

下面展示 Controller 中接收表單上傳文件和 ajax 異步提交上傳文件的方法,因為上傳文件需要進行 Post 提交,因此這兩個方法直接加上了 @PostMapping 注解,表示僅僅接收 Post 請求,具體內容如下:

package com.jobs.controller;

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

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

@RequestMapping("/test")
@Controller
public class TestController {

    //通過 form 表單上傳 2 個文件,分別用參數 file1 和 file2 接收
    @PostMapping("/formUpload")
    public String formUploadFile(HttpServletRequest request,
                                 String remark,
                                 MultipartFile file1,
                                 MultipartFile file2) throws IOException {

        System.out.println("提交過來的文件備注信息為:" + remark);

        /*
        //MultipartFile參數中封裝了上傳的文件的相關信息

        //獲取上傳文件的大小(單位:字節)
        System.out.println(file.getSize());
        //獲取上傳文件的字節數組
        System.out.println(file.getBytes());
        //獲取上傳文件的 mime 類型
        System.out.println(file.getContentType());
        //這個是參數名稱,一般不會使用
        System.out.println(file.getName());
        //獲取原始文件名稱
        System.out.println(file.getOriginalFilename());
        //判斷文件是否為空文件
        System.out.println(file.isEmpty());
        */

        //設置上傳后的文件保存的文件夾,如果不存在,就自動創建
        String dir = request.getServletContext().getRealPath("/upload");
        File directory = new File(dir);
        if (!directory.exists()) {
            directory.mkdirs();
        }

        //如果文件不為空的話,就寫入磁盤中
        if (file1 != null && !file1.isEmpty()) {

            //這里要求驗證單個上傳文件,不允許大於 2M
            long size = file1.getSize();
            if (size > 1 * 1024 * 1024) {
                request.setAttribute("msg", "file1上傳的文件大於2M");
                return "fail";
            }

            //獲取所上傳文件的原始文件名
            //你也可以生成一個隨機的文件名進行保存
            //這里的 demo 為了演示,就直接使用原始文件名進行寫入磁盤了
            String fname = file1.getOriginalFilename();
            //將文件寫入到磁盤中
            file1.transferTo(new File(dir, fname));
        }

        if (file2 != null && !file2.isEmpty()) {

            //這里要求驗證單個上傳文件,不允許大於 2M
            long size = file2.getSize();
            if (size > 1 * 1024 * 1024) {
                request.setAttribute("msg", "file2上傳的文件大於2M");
                return "fail";
            }

            String fname = file2.getOriginalFilename();
            file2.transferTo(new File(dir, fname));
        }

        return "success";
    }

    //通過 ajax 表單上傳 2 個文件,分別用參數 file1 和 file2 接收
    @PostMapping("/ajaxUpload")
    @ResponseBody
    public String ajaxUploadFile(HttpServletRequest request,
                                 String remark,
                                 MultipartFile file1,
                                 MultipartFile file2) throws IOException {

        System.out.println("提交過來的文件備注信息為:" + remark);

        //設置上傳后的文件保存的文件夾,如果不存在,就自動創建
        String dir = request.getServletContext().getRealPath("/upload");
        File directory = new File(dir);
        if (!directory.exists()) {
            directory.mkdirs();
        }

        //如果文件不為空的話,就寫入磁盤中
        if (file1 != null && !file1.isEmpty()) {

            //這里要求驗證單個上傳文件,不允許大於 2M
            long size = file1.getSize();
            if (size > 1 * 1024 * 1024) {
                return "file1上傳的文件大於2M";
            }

            //獲取所上傳文件的原始文件名
            //你也可以生成一個隨機的文件名進行保存
            //這里的 demo 為了演示,就直接使用原始文件名進行寫入磁盤了
            String fname = file1.getOriginalFilename();
            //將文件寫入到磁盤中
            file1.transferTo(new File(dir, fname));
        }

        if (file2 != null && !file2.isEmpty()) {

            //這里要求驗證單個上傳文件,不允許大於 2M
            long size = file2.getSize();
            if (size > 1 * 1024 * 1024) {
                return "file2上傳的文件大於2M";
            }

            String fname = file2.getOriginalFilename();
            file2.transferTo(new File(dir, fname));
        }

        return "ajax上傳文件成功";
    }
}

從上面的代碼可以看出,SpringMvc 僅僅需要一行代碼(使用 MultipartFile 的 transferTo 方法)就實現了文件上傳,非常簡單。

配合測試文件上傳的頁面,就是本 Demo 網站的主頁 index.html ,具體內容如下:

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>SpringMvc上傳文件演示</title>
</head>
<body>
<fieldset>
    <legend>Form 表單上傳文件</legend>
    <form action="/test/formUpload" method="post" enctype="multipart/form-data">
        文件信息備注:<input type="text" name="remark"><br/>
        上傳文件1:<input type="file" name="file1"><br/>
        上傳文件2:<input type="file" name="file2"><br/>
        <input type="submit" value="上傳">
    </form>
</fieldset>
<hr/>
<fieldset>
    <legend>Ajax 異步上傳文件</legend>
    文件信息備注:<input type="text" id="myRemark"><br/>
    上傳文件1:<input type="file" id="myFile1"><br/>
    上傳文件2:<input type="file" id="myFile2"><br/>
    <input type="button" value="上傳" id="btnUpload">
</fieldset>
<script src="./js/jquery-3.6.0.min.js"></script>
<script src="./js/upload.js"></script>
</body>
</html>

需要注意的是:對於 Form 表單上傳文件的方式,其必須要有 enctype="multipart/form-data" 這個屬性。

下面列出 ajax 上傳文件的 js 內容:

$(function () {
    $('#btnUpload').click(function () {
        //獲取 remark 信息
        var remark = $('#myRemark').val();
        //獲取第一個文件
        var f1 = $('#myFile1')[0].files[0];
        //獲取第二個文件
        var f2 = $('#myFile2')[0].files[0];

        //采用 FormData 組織數據
        var formdata = new FormData();
        formdata.append("remark", remark);
        formdata.append("file1", f1);
        formdata.append("file2", f2);

        $.ajax({
            type: "post",
            url: "/test/ajaxUpload",
            dataType: "json",
            data: formdata,
            cache: false,   //上傳文件無需緩存
            processData: false, //使數據不做處理
            contentType: false, //不要設置Content-Type請求頭
            success: function (data) {
                alert("返回的數據:" + data);
            }
        });
    });
})

需要注意的是:ajax 上傳文件,采用的是 FormData 進行組織數據,在提交的過程中,processData 和 contentType 必須設置為 false 。



到此為止,有關 SpringMvc 通過表單同步提交上傳文件和通過 ajax 異步提交上傳文件的代碼實現方式已經介紹完畢。

本博客 Demo 的源代碼下載地址為:https://files.cnblogs.com/files/blogs/699532/SpringMvc_FileUpload.zip




免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM