SpringBoot 中大文件(分片上传)断点续传与极速秒传功能的实现

网友投稿 1095 2023-05-15

SpringBoot 中大文件(分片上传)断点续传与极速秒传功能的实现

SpringBoot 中大文件(分片上传)断点续传与极速秒传功能的实现

1.创建SpringBoot项目

本项目采用springboot + mybatis-plus +jquery +thymeleaf组成

2.项目流程图

3.在pom中添加以下依赖

org.projectlombok

lombok

true

commons-io

commons-io

2.4

commons-fileupload

commons-fileupload

1.3.1

mysql

mysql-connector-java

runtime

com.baomidou

mybatis-plus-boot-starter

3.3.2

4.在application.properties配置文件中

spring.resources.static-locations=classpath:/static

server.port=8000

#设置上传图片的路径

file.basepath=D:/BaiduNetdiskDownload/

# 设置单个文件大小

spring.servlet.multipart.max-file-size= 50MB

# 设置单次请求文件的总大小

spring.servlet.multipart.max-request-size= 50MB

##设置要连接的mysql数据

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai

spring.datasource.username=root

spring.datasource.password=root

5.在数据库创建表

createhttp:// table file

(

id INTEGER primary key AUTO_INCREMENT comment 'id',

path varchar(100) not null COMMENT '相对路径',

name varchar(100) COMMENT '文件名',

suffix varchar(10) COMMENT '文件后缀',

size int COMMENT '文件大小|字节B',

created_at BIGINT(20) COMMENT '文件创建时间',

updated_at bigint(20) COMMENT '文件修改时间',

shard_index int comment '已上传分片',

shard_size int COMMENT '分片大小|B',

shard_total int COMMENT '分片总数',

file_key varchar(100) COMMENT '文件标识'

)

6.创建实体类

import com.baomidou.mybatisplus.annotation.IdType;

import com.baomidou.mybatisplus.annotation.TableId;

import com.baomidou.mybatisplus.annotation.TableName;

import lombok.Data;

@Data

@TableName(value = "file")

public class FileDTO {

/**

* id

*/

@TableId(value = "id", type = IdType.AUTO)

private Integer id;

/**

* 相对路径

*/

private String path;

/**

* 文件名

*/

private String name;

/**

* 后缀

*/

private String suffix;

/**

* 大小|字节B

*/

private Integer size;

/**

* 创建时间

*/

private Long createdAt;

/**

* 修改时间

*/

private Long updatedAt;

/**

* 已上传分片

*/

private Integer shardIndex;

/**

* 分片大小|B

*/

private Integer shardSize;

/**

* 分片总数

*/

private Integer shardTotal;

/**

* 文件标识

*/

private String fileKey;

}

7.创建mapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;

import com.example.demo.upload.entity.FileDTO;

import org.springframework.stereotype.Repository;

@Repository

public interface FileMapper extends BaseMapper {

}

8.创建service

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.example.demo.upload.dao.FileMapper;

import com.example.demo.upload.entity.FileDTO;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.util.List;

@Service

public class FileService {

@Autowired

private FileMapper fileMapper;

//保存文件

public void save(FileDTO file1){

//根据 数据库的 文件标识来查询 当前视频 是否存在

LambdaQueryWrapper lambda = new QueryWrapper().lambda();

lambda.eq(FileDTO::getFileKey,file1.getFileKey());

List fileDTOS = fileMapper.selectList(lambda);

//如果存在就话就修改

if(fileDTOS.size()!=0){

//根据key来修改

LambdaQueryWrapper lambda1 = new QueryWrapper().lambda();

lambda1.eq(FileDTO::getFileKey,file1.getFileKey());

fileMapper.update(file1,lambda1);

}else

{

//不存在就添加

fileMapper.insert(file1);

}

}

//检查文件

public List check(String key){

LambdaQueryWrapper lambda = new QueryWrapper().lambda();

lambda.eq(FileDTO::getFileKey,key);

List dtos = fileMapper.selectList(lambda);

return dtos;

}

}

9.创建utils

import lombok.Data;

/**

* 统一返回值

*

* @author zhangshuai

*

*/

@Data

public class Result {

// 成功状态码

public static final int SUCCESS_CODE = 200;

// 请求失败状态码

public static final int FAIL_CODE = 500;

// 查无资源状态码

public static final int NOTF_FOUNT_CODE = 404;

// 无权http://访问状态码

public static final int ACCESS_DINE_CODE = 403;

/**

* 状态码

*/

private int code;

/**

* 提示信息

*/

private String msg;

/**

* 数据信息

*/

private Object data;

/**

* 请求成功

*

* @return

*/

public static Result ok() {

Result r = new Result();

r.setCode(SUCCESS_CODE);

r.setMsg("请求成功!");

r.setData(null);

return r;

}

/**

* 请求失败

*

* @return

*/

public static Result fail() {

Result r = new Result();

r.setCode(FAIL_CODE);

r.setMsg("请求失败!");

r.setData(null);

return r;

}

/**

* 请求成功,自定义信息

*

* @param msg

* @return

*/

public static Result ok(String msg) {

Result r = new Result();

r.setCode(SUCCESS_CODE);

r.setMsg(msg);

r.setData(null);

return r;

}

/**

* 请求失败,自定义信息

*

* @param msg

* @return

*/

public static Result fail(String msg) {

Result r = new Result();

r.setCode(FAIL_CODE);

r.setMsg(msg);

r.setData(null);

return r;

}

/**

* 请求成功,自定义信息,自定义数据

*

* @param msg

* @return

*/

public static Result ok(String msg, Object data) {

Result r = new Result();

r.setCode(SUCCESS_CODE);

r.setMsg(msg);

r.setData(data);

return r;

}

/**

* 请求失败,自定义信息,自定义数据

*

* @param msg

* @return

*/

public static Result fail(String msg, Object data) {

Result r = new Result();

r.setCode(FAIL_CODE);

r.setMsg(msg);

r.setData(data);

return r;

}

public Result code(Integer code){

this.setCode(code);

return this;

}

public Result data(Object data){

this.setData(data);

return this;

}

public Result msg(String msg){

this.setMsg(msg);

return this;

}

}

10.创建controller

import com.example.demo.upload.entity.FileDTO;

import com.example.demo.upload.service.FileService;

import com.example.demo.upload.utils.Result;

import lombok.extern.slf4j.Slf4j;

import org.apache.commons.io.FilenameUtils;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.beans.factory.annotation.Value;

import org.springframework.stereotype.Controller;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.multipart.MultipartFile;

import java.io.*;

import java.util.List;

import java.util.UUID;

@Controller

@RequestMapping("/file")

@Slf4j

public class FileController {

@Autowired

FileService fileService;

public static final String BUSINESS_NAME = "普通分片上传";

// 设置图片上传路径

@Value("${file.basepath}")

private String basePath;

@RequestMapping("/show")

public String show(){

return "file";

}

/**

* 上传

* @param file

* @param suffix

* @param shardIndex

* @param shardSize

* @param shardTotal

* @param size

* @param key

* @return

* @throws IOException

* @throws InterruptedException

*/

@RequestMapping("/upload")

@ResponseBody

public String upload(MultipartFile file,

String suffix,

Integer shardIndex,

Integer shardSize,

Integer shardTotal,

Integer size,

String key

) throws IOException, InterruptedException {

log.info("上传文件开始");

//文件的名称

String name = UUID.randomUUID().toString().replaceAll("-", "");

// 获取文件的扩展名

String ext = FilenameUtils.getExtension(file.getOriginalFilename());

//设置图片新的名字

String fileName = new StringBuffer().append(key).append(".").append(suffix).toString(); // course\6sfSqfOwzmik4A4icMYuUe.mp4

//这个是分片的名字

String localfileName = new StringBuffer(fileName)

.append(".")

.append(shardIndex)

.toString(); // course\6sfSqfOwzmik4A4icMYuUe.mp4.1

// 以绝对路径保存重名命后的图片

File targeFile=new File(basePath,localfileName);

//上传这个图片

file.transferTo(targeFile);

//数据库持久化这个数据

FileDTO file1=new FileDTO();

file1.setPath(basePath+localfileName);

file1.setName(name);

file1.setSuffix(ext);

file1.setSize(size);

file1.setCreatedAt(System.currentTimeMillis());

file1.setUpdatedAt(System.currentTimeMillis());

file1.setShardIndex(shardIndex);

file1.setShardSize(shardSize);

file1.setShardTotal(shardTotal);

file1.setFileKey(key);

//插入到数据库中

//保存的时候 去处理一下 这个逻辑

fileService.save(file1);

//判断当前是不是最后一个分页 如果不是就继续等待其他分页 合并分页

if(shardIndex .equals(shardTotal) ){

file1.setPath(basePath+fileName);

this.merge(file1);

}

return "上传成功";

}

@RequestMapping("/check")

@ResponseBody

public Result check(String key){

List check = fileService.check(key);

//如果这个key存在的话 那么就获取上一个分片去继续上传

if(check.size()!=0){

return Result.ok("查询成功",check.get(0));

}

return Result.fail("查询失败,可以添加");

}

/**

* @author fengxinglie

* 合并分页

*/

private void merge(FileDTO fileDTO) throws FileNotFoundException, InterruptedException {

//合并分片开始

log.info("分片合并开始");

String path = fileDTO.getPath(); //获取到的路径 没有.1 .2 这样的东西

//截取视频所在的路径

path = path.replace(basePath,"");

Integer shardTotal= fileDTO.getShardTotal();

File newFile = new File(basePath + path);

FileOutputStream outputStream = new FileOutputStream(newFile,true); // 文件追加写入

FileInputStream fileInputStream = null; //分片文件

byte[] byt = new byte[10 * 1024 * 1024];

int len;

try {

for (int i = 0; i < shardTotal; i++) {

// 读取第i个分片

fileInputStream = new FileInputStream(new File(basePath + path + "." + (i + 1))); // course\6sfSqfOwzmik4A4icMYuUe.mp4.1

while ((len = fileInputStream.read(byt)) != -1) {

outputStream.write(byt, 0, len);

}

}

} catch (IOException e) {

log.error("分片合并异常", e);

} finally {

try {

if (fileInputStream != null) {

fileInputStream.close();

}

outputStream.close();

log.info("IO流关闭");

} catch (Exception e) {

log.error("IO流关闭", e);

}

}

log.info("分片结束了");

//告诉java虚拟机去回收垃圾 至于什么时候回收 这个取决于 虚拟机的决定

System.gc();

//等待100毫秒 等待垃圾回收去 回收完垃圾

Thread.sleep(100);

log.info("删除分片开始");

for (int i = 0; i < shardTotal; i++) {

String filePath = basePath + path + "." + (i + 1);

File file = new File(filePath);

boolean result = file.delete();

log.info("删除{},{}", filePath, result ? "成功" : "失败");

}

log.info("删除分片结束");

}

}

11.创建html页面

12.测试

12.2:查看数据库

12.3 :已经上传成功 重新上传一次

12.4:当我上传一半的时候 (KqsSzHCtDE显示上传了一个文件 然后我们继续上传)

12.5:继续上传 查看目录 发现已经成功了

13.注意事项

13.1:程序中并没有去判断 你存在不存在 上传图片的地址 所以如果打算使用代码 请创建 上传图片的目录 D:/BaiduNetdiskDownload/

13.2 :使用代码前请修改配置文件中的 数据库连接

14.代码 直达地址

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:IDEA创建springboot + mybatis项目全过程(步骤详解)
下一篇:SpringBoot整合Druid数据库连接池的方法
相关文章

 发表评论

暂时没有评论,来抢沙发吧~