【vue + springboot】切片+断点上传 + 秒传

需要了解透彻请参考:如何实现大文件上传、断点续传、切片上传_断点上传-CSDN博客

说明

切片:根据指定的大小对文件进行切块上传。

断点上传:每次上传完成一个切片后端保存信息,前端每次上传都判断所上传的文件是否存在,存在后端则返回信息,前端再根据信息进行调整继续上传。

秒传:如果文件已经在后端中存在,直接返回上传成功。可以节磁盘,提高用户体验,这里我用的是文件名来判断,不推荐,一般使用hash或者算法来为文件取一个唯一标识。

前端

axiosFun.sj

没有安装axios就在命令行安装:npm install axios

import axios from 'axios';

const req = (method, url, params) => {

return axios({

method: method,

url: url,

data: params,

headers: {

'Content-Type': 'multipart/form-data',

},

traditional: true,

}).then(res => res.data);

};

const jsonReq = (method, url) => {

return axios({

method: method,

url: url,

traditional: true,

}).then(res => res.data);

};

export { req, jsonReq };

request.js

import { req, jsonReq } from "./axiosFun";

export const upload = (params) => req('post', '/test/upload', params)

export const getUploadFile = (params) => jsonReq('get', '/test/getUploadFile/' + params)

type.js

class FileChunk {

constructor(chunk, fileName, start, end, total) {

// 切片对象

this.chunk = chunk;

// 文件名称

this.fileName = fileName;

// 切片起始位置

this.start = start;

// 切片结束位置

this.end = end;

// 文件总大小

this.total = total;

}

}

export default FileChunk;

HelloWorld.vue

<template>

<el-upload class="upload-demo" ref="upload" action="https://jsonplaceholder.typicode.com/posts/" :file-list="fileList"

:on-change="selectFile" :auto-upload="false" :limit="max_fileNum" :on-exceed="onExceed">

<el-button slot="trigger" size="small" type="primary">选取文件</el-button>

<el-button style="margin-left: 10px;" size="small" type="success" @click="submitUpload">上传到服务器</el-button>

</el-upload>

</template>

<script>

import FileChunk from './type.js';

import { upload, getUploadFile } from './request.js';

export default {

name: 'FileChunk',

data() {

return {

fileList: [],

max_fileNum: 1,

fileChunkSize: 10240

};

},

methods: {

createFileChunkList(start, end_) {

let fileChunkList = [];//文件块list

let file = this.fileList[0];//文件

let cur = null;//文件切割当前位置

let end = null;//文件切割结尾位置

//断点续传

if (start != null && end_ != null) {

let temp = start + this.fileChunkSize;

if (temp < file.size) {

cur = temp;

}

end = cur + this.fileChunkSize

if (end >= file.size) {

end = file.size

}

//从头开始切片上传

} else {

cur = 0;

end = this.fileChunkSize

}

//当前文件小于切块文件,直接返回

if (file.size < this.fileChunkSize) {

return fileChunkList.push(new FileChunk(file, file.name, cur, file.size));

} else {

//断点续传已切到最后一块

if (end == file.size) {

let blob = new Blob([file]).slice(cur, end);

const fileChunk = new FileChunk(blob, file.name, cur, end, file.size);

fileChunkList.push(fileChunk);

}

//切片

while (end < file.size) {

let blob = new Blob([file]).slice(cur, end);

const fileChunk = new FileChunk(blob, file.name, cur, end, file.size);

fileChunkList.push(fileChunk);

let temp = cur + this.fileChunkSize;

if (temp < file.size) {

cur = temp;

}

end = cur + this.fileChunkSize

if (end >= file.size) {

end = file.size

}

}

}

//返回切片list

return fileChunkList;

},

//上传

submitUpload() {

let fileChunkList = null;

//判断是否是断点上传的文件

getUploadFile(this.fileList[0].name).then(res => {

if (res.data == null) {//文件不存在,直接传

fileChunkList = this.createFileChunkList(null, null);

} else if (res.data.end == res.data.total) {//该文件已经存在了 -> 秒传

this.$message.success("上传成功")

return;

} else {//断点,文件续传

fileChunkList = this.createFileChunkList(res.data.start, res.data.end);

}

let reqNum = 0;//请求次数

let errCount = 3;//只允许错误三次

while (reqNum < fileChunkList.length && errCount > 0) {

const formData = new FormData();

formData.append("file", fileChunkList[reqNum].chunk);

formData.append("fileChunk", JSON.stringify(fileChunkList[reqNum]));

upload(

formData

).then(res => {

if (res) {

reqNum += 1;

} else {

errCount -= 1;

}

this.$message.success(res.data.msg)

});

}

})

},

selectFile(file) {

this.fileList.push(file.raw)

},

onExceed() {

this.$message.error("一次只能上传一个文件");

}

}

}

</script>./type.js

后端

操作数据库所需要的实体类、接口、mapper参考type.js创建一个表格,代码生成即可

返回值直接拷贝:CSDN

复制代码
package com.example.demo.controller;

import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.domain.FileChunk;
import com.example.demo.res.R;
import com.example.demo.service.FilechunkService;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.RandomAccessFile;

@RestController
@RequestMapping("/test")
@AllArgsConstructor
public class FileUploadController {

    private static final String UPLOAD_DIR = "D:\\luo\\code\\demo3\\src\\main\\resources\\files\\";
    private final FilechunkService filechunkService;

    @PostMapping("/upload")
    public R uploadFileChunk(@RequestParam("file") MultipartFile file, @RequestParam("fileChunk") String fileChunk) {
        FileChunk f = JSONUtil.toBean(fileChunk, FileChunk.class);
        String fullPath = UPLOAD_DIR + f.getFileName();
        // 模块写入对应的位置,rw表示读写模式
        try (RandomAccessFile rf = new RandomAccessFile(fullPath,
                "rw")) {
            rf.seek(f.getStart());
            rf.write(file.getBytes());
        } catch (Exception e) {
            return R.fail(e.getMessage());
        }
        //存储信息,一般存放在redis中,并设置在一定的时间内删除
        f.setId(1L);
        filechunkService.updateById(f);
        return R.success("上传成功");
    }

    @GetMapping("/getUploadFile/{name}")
    public R followUpload(@PathVariable("name") String name) {
        //查询数据库的信息,继续上传
        QueryWrapper<FileChunk> wrapper = new QueryWrapper<>();
        wrapper.eq("file_name", name);
        return R.data(filechunkService.getOne(wrapper));
    }

}
相关推荐
我有一个object19 分钟前
uniapp上传文件报错:targetSdkVersion设置>=29后在Android10+系统设备不支持当前路径。请更改为应用运行路径!
前端·javascript·vue.js·uniapp
_Kayo_29 分钟前
vue3 computed 练习笔记
前端·vue.js·笔记
AH_HH38 分钟前
Spring Boot 4.0 发布总结:新特性、依赖变更与升级指南
java·spring boot·后端
Tadas-Gao40 分钟前
Spring Boot 4.0架构革新:构建更精简、更安全、更高效的Java应用
java·spring boot·分布式·微服务·云原生·架构·系统架构
vx_bisheyuange1 小时前
基于SpringBoot的库存管理系统
java·spring boot·后端·毕业设计
艾小码1 小时前
Vue3 脚本革命:<script setup> 让你的代码简洁到飞起!
前端·javascript·vue.js
你不是我我1 小时前
【Java 开发日记】有了解过 SpringBoot 的参数配置吗?
java·开发语言·spring boot
q***49452 小时前
SpringBoot Maven 项目 pom 中的 plugin 插件用法整理
spring boot·后端·maven
菜泡泡@3 小时前
仓库地图vue-grid-layout
前端·javascript·vue.js
爱吃烤鸡翅的酸菜鱼3 小时前
Spring Boot 实现 WebSocket 实时通信:从原理到生产级实战
java·开发语言·spring boot·后端·websocket·spring