FastDfs大文件分片上传和断点续传
前言其实之前在大文件分片上传与极速秒传实现文章中我写过一次关于分片上传和断点续传, 但其实那个时候对断点续传的理解及其有限. 最近在项目里需要对大文件进行分片上传, 所以开始重新研究. 不过我觉得对于这个我只想讲下核心的思想.分片上传思想根据之前的文章, 我说过, 假如我们对500M的文件进行分片上传, 每片大小是5M, 那么总共就是100M分片. 前端主要就是通过文件大小和设置的分片的大小计算出
前言
其实之前在大文件分片上传与极速秒传实现文章中我写过一次关于分片上传和断点续传, 但其实那个时候对断点续传的理解及其有限. 最近在项目里需要对大文件进行分片上传, 所以开始重新研究. 不过我觉得对于这个我只想讲下核心的思想.
分片上传思想
根据之前的文章, 我说过, 假如我们对500M的文件进行分片上传, 每片大小是5M, 那么总共就是100M分片. 前端主要就是通过文件大小和设置的分片的大小计算出分片的数量, 将这些必要参数传给后端, 后端通过分片的数量一次一次的将每个分片传到后台, 后台也同样一次次的接收. 并记录到内存中, 如果中间出现闪断, 再次传文件时可以读取内存中的当前分片数, 继续进行上传
实现思路
上传校验
1.md5加密
通过文件名+文件大小+修改时间进行组合加密, 形成了这个文件的唯一标志
2.调用后台的check校验接口
首先进入接口去Redis中判断传入的md5值是否已经存在, 如果存在则拿里面的fileSize和taskId(这两个参数是我这个项目里的重要参数, fileSize是记录当前文件已经上传到的大小位置, taskID是这个任务的唯一Id由后台UUID生成).
如果没有就往redis中存入一个hash格式,key是md5值,存放文件大小(这个时候文件大小当然是0)和taskID(这个时候用UUID生成一下存进去). 并通过HashMap返回给前端.
ps: 这个项目中因为文件的分片实体是以taskID作为上传任务的唯一标志符, 所以还要以taskID为key存md5值. 当然实现分片的形式还有其他, 但是大致思想都与我上面说的一样!
文件上传
1.前端分片计算
总片数
Math.ceil(size / shardSize);
每块分片结束的位置
Math.min(file.size, start + shardSize);
这是取最小值, 如果当前分片已经走到了最后一片, 那么start(倒数第二块分片)+shardSize一定会大于文件大小, 所以这个时候取得就是当前文件大小
文件分片
file.slice(start, end);
具体请见前言里的上一篇文章
FastDfs大文件分片上传
https://blog.csdn.net/qq_42910468/article/details/108607427?utm_source=app
这里面有十分详细,并且可以直接跑的代码, 而且这个例子对我十分有用, 他的工具类里面有一个本地分片的代码和一个远程分片的代码, 而我的项目里正好全都要用到. 在这里要十分感谢作者提供的代码~
分片上传代码
@PostMapping(value = "/fastDfsChunkUpload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Map chunkUpload1(MultipartFileParam multipartFileParam, HttpServletResponse response) {
Map<String, String> map = new HashMap<>();
long chunk = multipartFileParam.getChunkNumber();
long totalChunk = multipartFileParam.getTotalChunks();
long chunkSize = multipartFileParam.getChunkSize();
long historyUpload = (chunk - 1) * chunkSize;
String md5 = multipartFileParam.getIdentifier();
MultipartFile file = multipartFileParam.getFile();
String fileName = FileUtil.extName(file.getOriginalFilename());
StorePath path = null;
String groundPath;
try {
if (chunk == 1) {
path = appendFileStorageClient.uploadAppenderFile(UpLoadConstant.DEFAULT_GROUP, file.getInputStream(),
file.getSize(), fileName);
if (path == null) {
map.put("result", "上传第一个就错了");
response.setStatus(500);
return map;
} else {
redisUtil.setObject(UpLoadConstant.uploadChunkNum + md5, 1, cacheTime);
map.put("result", "上传成功");
}
groundPath = path.getPath();
redisUtil.setObject(UpLoadConstant.fastDfsPath + md5, groundPath, cacheTime);
} else {
groundPath = (String) redisUtil.getObject(UpLoadConstant.fastDfsPath + md5);
appendFileStorageClient.modifyFile(UpLoadConstant.DEFAULT_GROUP, groundPath, file.getInputStream(),
file.getSize(), historyUpload);
Integer chunkNum = (Integer) redisUtil.getObject(UpLoadConstant.uploadChunkNum + md5);
chunkNum = chunkNum + 1;
redisUtil.setObject(UpLoadConstant.uploadChunkNum + md5, chunkNum, cacheTime);
}
Integer num = (Integer) redisUtil.getObject(UpLoadConstant.uploadChunkNum + md5);
if (totalChunk == num) {
response.setStatus(200);
map.put("result", "上传成功");
map.put("path", groundPath);
redisUtil.del(UpLoadConstant.uploadChunkNum + md5);
redisUtil.del(UpLoadConstant.fastDfsPath + md5);
}
} catch (FdfsIOException | SocketTimeoutException e) {
response.setStatus(407);
map.put("result", "重新发送");
return map;
} catch (Exception e) {
e.printStackTrace();
redisUtil.del(UpLoadConstant.uploadChunkNum + md5);
redisUtil.del(UpLoadConstant.fastDfsPath + md5);
response.setStatus(500);
map.put("result", "upload error");
return map;
}
System.out.println("result=" + map.get("result"));
System.out.println("path=" + map.get("path"));
return map;
}
校验的方法, 自行补充!
PS
https://blog.csdn.net/qq_42910468/article/details/108607427?utm_source=app 这个例子一定要看,简单易懂还能跑, 是基于fastdfs的分片上传, 而且也有本地的分片上传. 不过断点续传他没有写, 所以你也别想直接抄过来, 要上下两个栗子容在一起才能实现大文件的分片上传和断点续传!
更多推荐
所有评论(0)