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实现文件上传

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