SpringBoot中Form表單和Ajax實現文件上傳

一、源碼解讀

瞭解源碼,才能更好得學習,看多了源碼,也發現自己越來越菜,難受!好了,廢話不多說,下面開始學習吧。

【CommonsMultipartResolver.java】

如果有小夥伴做過關於文件上傳的功能,相信對這個類絕不陌生。我先簡單介紹一下它,在Servlet3.0之前,基本上都是用它,但是這個工具雖然對版本的兼容性比較好,但是它的缺點也十分明顯,需要依賴其他jar包,如下:

  1. common-fileupload.jar
  2. common-io.jar

上面這兩者的jar包版本我就不做過多解釋了,因爲我們現在很幸福,有Maven幫助我們管理依賴的版本,有了SpringBoot之後,我們更幸福啦!因爲常用的jar包,我們都不需要關係它的版本了,好像有點囉嗦,迴歸正題。

下面我們來看看CommonsMultipartResolver的源碼:
在這裏插入圖片描述
在網上查資料找到一篇對這個工具分析比較全面和仔細的博客,這裏我分享給大家,Dispatcher的重要組件之一MultipartResolver(StandardServletMultipartResolver和CommonsMultipartResolver)的用法

我想講的是它所實現的實現的接口MultipartResolver,下面打開這個接口的結構看看,都有哪些實現類,如下:
在這裏插入圖片描述
可以看到,它有兩個實現類,所以這意味着我們在選擇工具的時候又多了一種選擇,對,就是它了StandardServletMultipartResolver。接下來,就來了解一下爲什麼我們要選擇它。首先,StandardServletMultipartResolver在Servlet3.0之後就支持了,而且它不用依賴其他jar包,是不是很方便?那就有小夥伴問了,在什麼環境下可以使用這個工具呢,其實從Tomcat7.0之後就支持Servlet3.0了,SpringBoot就更不用多說了,畢竟它更牛,內嵌Tomcat,所以大家可以放心的使用。接下來,我就詳細介紹一下這個工具如何使用。

二、單個文件上傳

開始之前,先簡單說一下,單文件上傳我會用Form表單和Ajax分別舉例,後面多文件上傳也是如此。

方法一:Form表單

先來完成後端代碼編寫,其實後端代碼邏輯也比較簡單。首先,先定義上傳文件的目錄路徑,如果該目錄不存在就先創建,然後,爲了避免文件重名,需要給它改個名字,這裏如果對上傳的文件名字沒有什麼要求可以使用UUID等工具類來隨機生成,如果上傳的文件名稱需要在頁面顯示,也可以使用MD5等可逆的加密算法先改名回顯時再解密即可,最後如果需要寫到硬盤上,多做一步持久化操作即可,在這裏我就不做這步操作了,這裏我就返回該文件的路徑了。

【FileUploadController.java】

package com.mango.fileupload;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@RestController
public class FileUploadController {

    SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/");

    @PostMapping("/upload")
    public String upload(MultipartFile file, HttpServletRequest request) {
        //1.定義上傳文件目錄
        String format = sdf.format(new Date());
        String path = request.getServletContext().getRealPath("/img") + format;
        File folder = new File(path);
        if (!folder.exists()) {
            folder.mkdirs();
        }

        //2.獲取文件信息
        String oldName = file.getOriginalFilename();
        String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
        try {
            file.transferTo(new File(folder, newName));

            //3.返回上傳文件的url
            String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/img" + format + newName;
            return url;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "error";
    }
}

【index.html】

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
    <input type="file" name="file">
    <input type="submit" value="提交">
</form>
</body>
</html>

注:form表單上的enctype="multipart/form-data"屬性是必須要加上的,不然後臺接收前端的請求不知道你是上傳文件的操作。

【運行結果】
在這裏插入圖片描述
方法二:Ajax

上面的java文件不需要做更改,我們新建一個html文件,用於實現ajax方式提交,如下:

【index2.html】

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery-3.3.1.js"></script>
</head>
<body>
<div id="result"></div>
<input type="file" id="file">
<input type="button" value="上傳" onclick="uploadFile()">
<script>
    function uploadFile() {
        var file = $("#file")[0].files[0];
        var formData = new FormData();
        formData.append("file",file);
        $.ajax({
            type:'post',
            url:'/upload',
            processData: false,
            contentType:false,
            data:formData,
            success:function (msg) {
                $("#result").html(msg);
            }
        })
    }
</script>
</body>
</html>

注:

  1. processData:需要設置爲false,默認情況下,processData 的值是 true,含義是以對象的形式提交的數據都會被轉換爲字符串的形式提交,也就是application/x-www-form-urlencoded,而當上傳文件的時候,則不需要把其轉換爲字符串,因此要改成false
  2. contentType:需要設置爲false,有個類似的屬性是 dataType , 含義是希望從服務端返回的數據的格式,一般會有 json 、text……等。而 contentType 則是與 dataType 相對應的,含義是前端發送數據的格式。默認值爲:application/x-www-form-urlencoded,含義是 ajax 的 data 是以字符串的形式,如: id=2020&password=123456,使用這種傳數據的格式,無法傳輸複雜的數據,如:多維數組、文件等。有時需要注意,自己所傳輸的數據格式和ajax的contentType格式是否一致,如果不一致就要想辦法對數據進行轉換。 把contentType 改成 false 就會去除之前默認的數據格式,在上傳文件時就不會報錯了。

使用ajax上傳文件的運行結果和上面使用form表單效果相同,這裏就不再展示了。

三、多個文件上傳

方法一:Form表單

既然是多文件上傳,那麼服務端的代碼就需要稍作調整了,因爲上傳多個文件,服務端接收到的就是文件數組了,我們需要對這個文件數組中的每個數據進行操作,其實我將這個原理說清楚了,小夥伴應該就知道該怎麼做了,對吧?

我就不在服務端另外創建文件了,單獨再寫一個方法即可,廢話不多說,開始吧。

FileUploadController.java

package com.mango.fileupload;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

@RestController
public class FileUploadController {

    SimpleDateFormat sdf = new SimpleDateFormat("/yyyy/MM/dd/");

    @PostMapping("/uploads")
    public String uploads(MultipartFile[] files, HttpServletRequest request) {
        //1.定義上傳文件目錄
        String format = sdf.format(new Date());
        String path = request.getServletContext().getRealPath("/img") + format;
        File folder = new File(path);
        if (!folder.exists()) {
            folder.mkdirs();
        }

        for (MultipartFile file : files) {
            //2.獲取文件信息
            String oldName = file.getOriginalFilename();
            String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."));
            try {
                file.transferTo(new File(folder, newName));

                //3.返回上傳文件的url
                String url = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + "/img" + format + newName;
                System.err.println(url);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return "success";
    }
}

注:其實,大家也明白了,無非就是遍歷這個文件數組,對每個文件進行操作即可。再囉嗦一下,因爲多文件上傳,這裏我稍作改動了一下,之前是在瀏覽器上輸出文件路徑,這裏改成了只在瀏覽器頁面返回success,文件的路徑通過服務端的控制檯輸出了。

下面來聊聊前端該如何處理,使用form表單提交,修改比較容易,只需要在選擇文件的標籤上添加multiple屬性即可,這樣選擇文件時就可以進行單選/多選了。

【index3.html】

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form action="/uploads" method="post" enctype="multipart/form-data">
    <input type="file" name="files" multiple>
    <input type="submit" value="提交">
</form>
</body>
</html>

【運行結果】
在這裏插入圖片描述
注:這裏大家可以留意下瀏覽器url有什麼變化,分析下form和ajax運行後不同的變化,至於爲什麼產生這種情況,小夥伴可以試着找下答案。(其實非常簡單)

方法二:Ajax

【index4.html】

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/jquery-3.3.1.js"></script>
</head>
<body>
<div id="result"></div>
<input type="file" id="file" multiple>
<input type="button" value="上傳" onclick="uploadFile()">
<script>
    function uploadFile() {
        var files = $("#file")[0].files;
        console.log(files);
        var formData = new FormData();
        for (var i = 0; i < files.length; i++) {
            formData.append("files", files[i]);
        }
        $.ajax({
            type: 'post',
            url: '/uploads',
            processData: false,
            contentType: false,
            data: formData,
            success: function (msg) {
                $("#result").html(msg);
            }
        })
    }
</script>
</body>
</html>

注:formData在進行append操作時,只能逐個進行操作。

【運行結果】
在這裏插入圖片描述

四、源碼地址

GitHub源碼地址:SpringBoot中Form表單和Ajax實現文件上傳

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章