接上篇文章 java 超大文件分片上传 在其基础上继续实现 断点续传和文件秒传功能
在上篇中,我们可以使用 file. slice 方法对文件进行分片,可以从后台读到当前文件已经上传的大小,就可以知道从哪里开始切片,断点续传的原理就是基于这个的。
前端计算文件的 md5 ,后台数据库查询一遍(前提是把 md5 存储了,计算文件 md5 也是需要消耗时间的)即可知道是否有相同文件,这是实现文件秒传的方法。
可能存在的问题:
- 有两个人同时在上传同一个文件,但前一个人还没有上传完成,此时第二个文件认为是新文件不能秒传
- 此时获取文件原数据时需要将文件信息保存起来,重点是要保存 md5 ,保证一个文件的 md5 保计算一次
- 获取断点文件时,真实的文件上传位置应该是从文件系统中读出来的
根据需求说明,后台应该存在四个接口,获取文件信息(包含是否可以秒传),获取断点文件列表,分片上传接口,文件完整性验证
全部源码位置 : https://gitee.com/sanri/example/tree/master/test-mvc
/**
* 加载断点文件列表
* @return
*/
@GetMapping("/breakPointFiles")
public List breakPointFiles(){
List fileInfoPos = fileMetaDataRepository.breakPointFiles();
return fileInfoPos;
}
/**
* 获取文件元数据,判断文件是否可以秒传
* @param originFileName
* @param fileSize
* @param md5
* @return
* @throws URISyntaxException
*/
@GetMapping("/fileMetaData")
public FileMetaData fileMetaData(String originFileName, Long fileSize, String md5) throws URISyntaxException, MalformedURLException {
FileMetaData similarFile = bigFileStorage.checkSimilarFile(originFileName,fileSize, md5);
if(similarFile != null){
similarFile.setSecUpload(true);
// 如果文件名不一致,则创建链接文件
if(!similarFile.getOriginFileName() .equals(originFileName)) {
bigFileStorage.createSimilarLink(similarFile);
}
return similarFile;
}
//获取文件相关信息
String baseName = FilenameUtils.getBaseName(originFileName);
String extension = FilenameUtils.getExtension(originFileName);
String finalFileName = bigFileStorage.rename(baseName, fileSize);
if(StringUtils.isNotEmpty(extension)){
finalFileName += ("."+extension);
}
URI relativePath = bigFileStorage.relativePath(finalFileName);
//如果没有相似文件,则要创建记录到数据库中,为后面断点续传做准备
FileInfoPo fileInfoPo = new FileInfoPo();
fileInfoPo.setName(originFileName);
fileInfoPo.setType(extension);
fileInfoPo.setUploaded(0);
fileInfoPo.setSize(fileSize);
fileInfoPo.setRelativePath(relativePath.toString());
fileInfoPo.setMd5(md5);
fileMetaDataRepository.insert(fileInfoPo);
URI absoluteURI = bigFileStorage.absolutePath(relativePath);
FileMetaData fileMetaData = new FileMetaData(originFileName, finalFileName, fileSize, relativePath.toString(), absoluteURI.toString());
fileMetaData.setMd5(md5);
fileMetaData.setFileType(extension);
return fileMetaData;
}
/**
* 获取当前文件已经上传的大小,用于断点续传
* @return
*/
@GetMapping("/filePosition")
public long filePosition(String relativePath) throws IOException, URISyntaxException {
return bigFileStorage.filePosition(relativePath);
}
/**
* 上传分段
* @param multipartFile
* @return
*/
@PostMapping("/uploadPart")
public long uploadPart(@RequestParam("file") MultipartFile multipartFile, String relativePath) throws IOException, URISyntaxException {
bigFileStorage.uploadPart(multipartFile,relativePath);
return bigFileStorage.filePosition(relativePath);
}
/**
* 检查文件是否完整
* @param relativePath
* @param fileSize
* @param md5
* @return
*/
@GetMapping("/checkIntegrity")
public void checkIntegrity(String relativePath,Long fileSize,String fileName) throws IOException, URISyntaxException {
long filePosition = bigFileStorage.filePosition(relativePath);
Assert.isTrue(filePosition == fileSize ,"大文件上传失败,文件大小不完整 "+filePosition+" != "+fileSize);
String targetMd5 = bigFileStorage.md5(relativePath);
FileInfoPo fileInfoPo = fileMetaDataRepository.selectByPrimaryKey(fileName);
String md5 = fileInfoPo.getMd5();
Assert.isTrue(targetMd5.equals(md5),"大文件上传失败,文件损坏 "+targetMd5+" != "+md5);
//如果文件上传成功,更新文件上传大小
fileMetaDataRepository.updateFilePosition(fileName,filePosition);
}
重要的处理部分其实还是前端,下面看前端的代码,需要使用到一个计算 md5 值的库 spark-md5.js
大文件批量上传,支持断点续传,文件秒传
文件名
文件大小
已上传大小
相对路径
md5
源码位置: https://gitee.com/sanri/example/tree/master/test-mvc
一点小推广
创作不易,希望可以支持下我的开源软件,及我的小工具,欢迎来 gitee 点星,fork ,提 bug 。
Excel 通用导入导出,支持 Excel 公式
博客地址:https://blog.csdn.net/sanri1993/article/details/100601578
gitee:https://gitee.com/sanri/sanri-excel-poi
使用模板代码 ,从数据库生成代码 ,及一些项目中经常可以用到的小工具
博客地址:https://blog.csdn.net/sanri1993/article/details/98664034
gitee:https://gitee.com/sanri/sanri-tools-maven