最近想实现一下分片上传的功能,文件分片上传就能实现断点续传了,数据库记录也能保证秒传功能的实现。记录一下宝宝demo。
前端部分可以看看这篇文章:Springboot实现文件分片断点续传功能(前端篇)
项目代码已上传至GitHub。 https://github.com/huiluczP/segment_upload
整体思路
- 首先利用数据库存储文件信息,包括文件物理地址,分片接收进程和对应的md5码。利用md5码可以判断当前上传文件是否在服务器中存在(实现秒传),利用分片接收Index可以判断现在应该上传。
- 前端ajax获取文件存在与否的信息,几种情况:
(1) 不存在,则创建数据库记录,成功后调用分片1的上传。
(2) 存在,Index和总分片数量相同,秒传成功显示结果。
(3) 存在,但index小于总分片数量,调用分片index的上传。 - 分片在前端根据分片Index计算起点末尾,slice切割,ajax调用上传传到服务器并存储。当前分片传递成功,ajax接收success信息,串行进行index+1的分片的上传。
具体后端实现使用的是springboot+mybatis。
数据库设计和持久化操作
主要就是segment_file
表,用来存储文件对应信息。
其中file_path
表示文件最终的物理路径(未完成的分片文件也会存储在parent文件夹中)。
file_name
是uuid,segment_index
是已经完成的分片数量,segment_total
为总的分片数量。
md5_key
是文件对应的唯一key,实现中是在前端计算的。
CREATE TABLE `segment_file` (
`id` int NOT NULL AUTO_INCREMENT,
`file_path` varchar(200) NOT NULL,
`file_name` varchar(200) DEFAULT NULL,
`size` int DEFAULT NULL,
`segment_index` int DEFAULT NULL,
`segment_size` int DEFAULT NULL,
`segment_total` int DEFAULT NULL,
`md5_key` varchar(200) DEFAULT NULL,
PRIMARY KEY (`id`)
)
持久化类 SegmentFile
public class SegmentFile implements Serializable {
private static final long serialVersionUID = -1937877331354085546L;
private int id;
private String filePath;
private String fileName;
private int size;
private int segmentIndex;
private int segmentSize;
private int segmentTotal;
private String md5Key;
getter…
setter…
}
Mapper接口,这边使用注解进行mybatis sql语句的配置.
@Mapper
public interface SegmentFileMapper {
// 获取对应的分片文件实体类
@Select("select * from segment_file where md5_key = #{key}")
@Results(id="segmentFileResult",value={
@Result(id=true, column = "id",property = "id"),
@Result(column = "file_path",property = "filePath"),
@Result(column = "file_name",property = "fileName"),
@Result(column = "size",property = "size"),
@Result(column = "segment_index",property = "segmentIndex"),
@Result(column = "segment_size",property = "segmentSize"),
@Result(column = "segment_total",property = "segmentTotal"),
@Result(column = "md5_key",property = "md5Key")
})
public List<SegmentFile> getSegmentFileByKey(String key);
// 添加对应的文件实体类
@Insert("insert into segment_file(id,file_path,file_name," +
"size,segment_index,segment_size,segment_total,md5_key) " +
"values(#{id},#{filePath},#{fileName},#{size},#{segmentIndex}," +
"#{segmentSize},#{segmentTotal},#{md5Key})")
public int insertSegmentFile(SegmentFile segmentFile);
// 主要用来更新分片信息
@Update({
"update segment_file set " +
"file_path = #{filePath},file_name = #{fileName},size = #{size}," +
"segment_index = #{segmentIndex}, segment_size = #{segmentSize}," +
"segment_total = #{segmentTotal}, md5_key = #{md5Key}" +
"where id = #{id}" })
public int updateSegmentFile(SegmentFile segmentFile);
}
特别的,我们对文件保存路径在配置文件application.yml
中进行设置。同时,为了实现大文件分片的上传,关闭springboot的文件上传大小限制。数据库配置就不赘述了。
spring:
servlet:
multipart:
max-file-size: -1
max-request-size: -1
file:
save-path: E:/file/
工具类实现
FileUtil为文件处理工具类,包括利用uuid和md5码的文件名生成。特别的,我们将原文件的名称后加上#
和分片序号作为对应分片文件的名称。
// 工具类
// 文件名生成
public class FileUtil {
public static String getFileNameWithoutSuffix(String fileName){
int suffixIndex = fileName.lastIndexOf('.');
if(suffixIndex<