全栈小白的gravatar头像
全栈小白 2023-03-15 14:51:31
一文学会文件上传-第二节:SpringBoot+ajax方式

原创声明:本人所发内容及涉及源码,均为亲手所撸,如总结内容有误,欢迎指出

唠嗑部分

随着前后端分离的开发方式,Form方式越来越少见了,对于后端工程师来说,接口层并没有多大变化,对于前端工程师、亦或者是干全栈的同学们来说,ajax已经不再陌生,那我们本节就来说说,ajax实现文件上传及文件预览

首先来说FormData与FileReader的使用,都是HTML5新增的API

FormData

FormData 接口提供了一种表示表单数据的键值对 key/value 的构造方式,并且可以轻松的将数据通过XMLHttpRequest.send()方法发送出去,如果发送请求时的编码类型被设为 "multipart/form-data",它会使用和表单一样的格式。

构造函数

// 参数传form表单对象,要求表单中的每一项均有name字段,否则无法帮你做收集
var fd = new FormData(form)

常用api

fd.append(key,value);   // 如果指定的key不存在则会新增一条数据,如果key存在,则追加到数据末尾
fd.set(key,value);      // 如果指定的key存在则会覆盖
fd.get(key);      // 返回该key的第一个值
fd.getAll(key);   // 返回该key的所有值,数组格式
fd.delete(key);   // 删除一项
fd.has(key);     // 判断key是否存在,返回布尔值

FileReader

FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件内容,使用 File 或 Blob 对象指定要读取的文件或数据。

构造函数

const fr = new FileReader(); 

常用api

fr.readAsDataURL(file);   // 参数是一个文件,将文件读取为一个Base64编码的字符串,通过fr.result获取
fr.onload = () => {};     // 事件,读取过程是异步的,读取完会执行

言归正传

1、创建项目&导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>spring-boot-starter-parent</artifactId>
        <groupId>org.springframework.boot</groupId>
        <version>2.4.2</version>
        <relativePath/>
    </parent>
    <modelVersion>4.0.0</modelVersion>
​
    <groupId>com.cxs</groupId>
    <artifactId>ajax-file-upload</artifactId>
    <version>1.0-SNAPSHOT</version>
​
    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
    </dependencies>
</project>

2、编写前端页面结构

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ajax-文件上传</title>
    <script type="text/javascript" src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script>
</head>
<body>
    <div>
        <form id="form">
            用户名:<input type="text" name="username"/><br/>
            密码: <input name="password" type="password"/><br/>
            <input id="file" type="file" name="file" style="display: none;"/>
            <label for="file">
                <img id="img" src="" title="+" width="300" height="150" style="cursor: pointer;"/>
            </label>
            <button>上传</button>
        </form>
    </div>
</body>
</html>

3、后端接口部分

@PostMapping("/upload")
public Map<String, Object> upload(User user, @RequestParam("file") MultipartFile file){
    Map<String, Object> result = new HashMap<>();
    result.put("code", 200);
    try {
        if (!ObjectUtils.isEmpty(file)) {
            // 获取源文件的名字
            String originalFilename = file.getOriginalFilename();
            if (StringUtils.hasLength(originalFilename)) {
                // 进行文件上传
                String fullPath = PATH + File.separator + originalFilename;
                File f = new File(fullPath);
                if (!f.exists()) {
                    f.createNewFile();
                }
                file.transferTo(f);
                user.setAvatar(fullPath);
                result.put("data", fullPath);
                result.put("user", user.toString());
            }
        } else {
            result.put("code", 400);
            result.put("msg", "文件不能为空!");
        }
    } catch (IOException e) {
        e.printStackTrace();
        result.put("code", 500);
        result.put("msg", "文件上传失败!");
    }
    return result;
}

4、文件预览-FileReader的使用

详解请看注释

// 给input框添加change事件,监听文件的改变
inp.addEventListener("change",function(){
    // 获取到文件,inp.files是一个集合
    const file = inp.files[0];
    // 基础校验
    if(!file) {
        // 点击了取消,将预览置空
        img.src = currentPath;
        // 清空currentFile
        currentFile = undefined;
        return;
    }
    // 文件格式判断,这里只能传图片
    if(!file.type.startsWith("image")) return window.alert('请选择图片');
    // 给currentFile赋值
    currentFile = file;
    // 读取文件,进行预览
    const fr = new FileReader();
    // 将文件读取为一个base64编码
    fr.readAsDataURL(file);
    // fr读取是一个异步操作,读取完会执行下面函数
    fr.onload = () => {
        console.log(fr.result)
        img.src = fr.result;
    }
})

5、文件上传-FormData的使用

Jquery对于ajax的处理说明:

jQuery帮我们做了封装,会将所有请求的请求头设置为application/x-www-form-urlencoded,而文件上传要求是multipart/form-data

此时如果按照jQuery默认的就会出现问题

解决:不让jQuery帮我们设置就可以:contentType:false,告诉jQuery不要设置我请求头里的content-type

jQuery还会帮我们把数据格式转化为key=value的形式

解决:不让jQuery自动格式化数据:processData:false,告诉jQuery别帮我格式化数据

// 表单的提交事件
$("#form").submit(function (e){
    // 阻止表单submit事件的默认行为,否则会有个刷新的现象
    e.preventDefault()
    // 创建formdata,FormData会将form表单中带有name的字段自动处理成每一项
    var formData = new FormData(this);
    if(!currentFile) return window.alert("请选择文件再上传");
    // 发送ajav请求上传
    $.ajax({
        // 服务器接口地址
        url: '/upload',
        // 请求方式
        type: 'post',
        // 参数
        data: formData,
        // 这两个参数在文件上传时较特殊,详细请看文章描述
        contentType : false,
        processData : false,
        // 成功的回调函数
        success : (resp) => {
            console.log(resp)
        }
    })
})

测试&结语

测试如下图

一文学会文件上传-第二节:SpringBoot+ajax方式

结语

1、本文采用jQuery作为ajax库,axios大家可自行研究(类似)

2、源码获取方式,https://gitee.com/whole-stack-of-white/file_upload_demo


打赏

已有2人打赏

dafeiyu的gravatar头像 最代码官方的gravatar头像
最近浏览
stedian  LV4 2023年12月20日
叶儿飞  LV4 2023年11月29日
易拉罐  LV8 2023年10月8日
szy0077  LV4 2023年5月8日
lijunxiang  LV2 2023年4月27日
wwswdgyqd  LV6 2023年4月10日
dafeiyu  LV10 2023年4月7日
Yanxigul 2023年4月7日
暂无贡献等级
qhdjod  LV7 2023年3月22日
全栈小白  LV31 2023年3月18日
顶部 客服 微信二维码 底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友