当前位置:   article > 正文

大文件分片上传与断点续传_fastdfs分片上传和断点续传

fastdfs分片上传和断点续传

由于java天生缺陷 但是项目需要上传大文件,所以做了一个分片上传,话不多说.............................

UploadFileUtil.java 工具包 utils层

  1. /**
  2. * 分片上传与断点续传
  3. *
  4. * @param multipartFileParam 分片实体
  5. * @param targetPath 目标路径
  6. * @return 待定
  7. */
  8. public ApiResult uploadAppendFile(MultipartFileParam multipartFileParam, String targetPath) {
  9. String[] bin=multipartFileParam.getFileName ().split ("\\.");
  10. Map<String, String> map = new HashMap<>();
  11. if(!bin[1].equals ("bin")){
  12. map.put("result", "文件格式错误");
  13. log.error ("文件格式不对,请检查升级文件");
  14. return ResultUtil.error (map);
  15. }
  16. long chunk = multipartFileParam.getChunkNumber();
  17. long totalChunks = multipartFileParam.getTotalChunks();
  18. long fileSize = multipartFileParam.getFileSize();
  19. String taskId = multipartFileParam.getTaskId();
  20. MultipartFile file = multipartFileParam.getFile();
  21. String fileName = multipartFileParam.getFileName();
  22. // String extName = FileUtil.extName(fileName);
  23. // String separator = FileUtil.FILE_SEPARATOR;
  24. String localPath = targetPath + separator;
  25. File tempFile = null;
  26. RandomAccessFile raf = null;
  27. InputStream is = null;
  28. try {
  29. if (chunk == 1) {
  30. String tempFileName = taskId + fileName.substring(fileName.lastIndexOf(".")) + "_tmp";
  31. File fileDir = new File(localPath);
  32. if (!fileDir.exists()) {
  33. fileDir.mkdirs();
  34. }
  35. tempFile = new File(localPath, tempFileName);
  36. if (!tempFile.exists()) {
  37. tempFile.createNewFile();
  38. }
  39. raf = new RandomAccessFile(tempFile, "rw");
  40. is = file.getInputStream();
  41. raf.seek(0);
  42. int len = 0;
  43. byte[] bytes = new byte[1024 * 10];
  44. while ((len = is.read(bytes)) != -1) {
  45. raf.write(bytes, 0, len);
  46. }
  47. raf.close();
  48. is.close();
  49. redisUtil.setObject(UpLoadConstant.chunkNum + taskId, chunk, cacheTime);
  50. redisUtil.setObject(UpLoadConstant.fastDfsPath + taskId, tempFile.getPath(), cacheTime);
  51. map.put("result", "上传成功");
  52. } else {
  53. String path = (String) redisUtil.getObject(UpLoadConstant.fastDfsPath + taskId);
  54. is = file.getInputStream();
  55. raf = new RandomAccessFile(path, "rw");
  56. raf.seek(fileSize);
  57. int len = 0;
  58. byte[] bytes = new byte[1024 * 10];
  59. while ((len = is.read(bytes)) != -1) {
  60. raf.write(bytes, 0, len);
  61. }
  62. redisUtil.setObject(UpLoadConstant.chunkNum + taskId, chunk, cacheTime);
  63. raf.close();
  64. is.close();
  65. }
  66. String md5 = (String) redisUtil.getObject(UpLoadConstant.task + taskId);
  67. HashMap<String, String> redisMap = new HashMap<>();
  68. redisMap.put("fileSize", fileSize + "");
  69. redisMap.put("taskId", taskId);
  70. redisUtil.setHashAsMap(UpLoadConstant.fileMd5 + md5, redisMap, cacheTime);
  71. if (chunk == totalChunks) {
  72. String path = (String) redisUtil.getObject(UpLoadConstant.fastDfsPath + taskId);
  73. // FileUtil.rename(new File(path), fileName, true);//改文件名称 原名
  74. FileUtil.rename(new File(path), "upgrade.bin", true);//指定名称 upgrade.bin
  75. map.put("result", "上传完毕");
  76. redisUtil.del(UpLoadConstant.fileMd5 + md5);
  77. redisUtil.del(UpLoadConstant.task + taskId);
  78. redisUtil.del(UpLoadConstant.chunkNum + taskId);
  79. redisUtil.del(UpLoadConstant.fastDfsPath + taskId);
  80. }
  81. } catch (IOException e) {
  82. e.printStackTrace();
  83. String md5 = (String) redisUtil.getObject(UpLoadConstant.task + taskId);
  84. redisUtil.del(UpLoadConstant.fileMd5 + md5);
  85. redisUtil.del(UpLoadConstant.task + taskId);
  86. redisUtil.del(UpLoadConstant.chunkNum + taskId);
  87. redisUtil.del(UpLoadConstant.fastDfsPath + taskId);
  88. map.put("result", "上传异常");
  89. } finally {
  90. try {
  91. if (raf != null) {
  92. raf.close();
  93. }
  94. } catch (IOException e) {
  95. e.printStackTrace();
  96. }
  97. try {
  98. if (is != null) {
  99. is.close();
  100. }
  101. } catch (IOException e) {
  102. e.printStackTrace();
  103. }
  104. }
  105. return ResultUtil.success(map);
  106. }
  107. /**
  108. * 校验md5值
  109. *
  110. * @param md5 md5
  111. * @return map
  112. */
  113. public Map<String, Object> checkMd5(String md5) {
  114. Map<String, Object> map = new HashMap<>();
  115. String fileSize = "";
  116. String taskId = "";
  117. md5 = SecureUtil.md5(md5);
  118. Map redisMap = redisUtil.getMap(UpLoadConstant.fileMd5 + md5);
  119. if (MapUtil.isNotEmpty(redisMap)) {
  120. fileSize = ( redisMap.get("fileSize").toString ());
  121. taskId = ( redisMap.get("taskId").toString ());
  122. }
  123. if (StrUtil.isNotEmpty(fileSize)) {
  124. map.put("fileSize", Long.parseLong(fileSize));
  125. } else {
  126. Map<String, Object> map1 = new HashMap<>();
  127. taskId = IdUtil.simpleUUID();
  128. map1.put("fileSize", 0);
  129. map1.put("taskId", taskId);
  130. redisUtil.setHashAsMap(UpLoadConstant.fileMd5 + md5, map1, cacheTime);
  131. redisUtil.setObject(UpLoadConstant.task + taskId, md5, cacheTime);
  132. map.put("fileSize", 0);
  133. }
  134. map.put("taskId", taskId);
  135. return map;
  136. }

RedisUtil.java 同为utils层

  1. package vip.xiaonuo.sys.modular.upgrade.utils;
  2. import org.springframework.data.redis.core.RedisTemplate;
  3. import org.springframework.stereotype.Component;
  4. import org.springframework.util.CollectionUtils;
  5. import javax.annotation.Resource;
  6. import java.util.List;
  7. import java.util.Map;
  8. import java.util.concurrent.TimeUnit;
  9. /**
  10. * @Author: geng
  11. * @Date: 2022/9/23 21:49
  12. * @Description:
  13. */
  14. @Component
  15. public class RedisUtil {
  16. @Resource
  17. private RedisTemplate<String, Object> redisTemplate;
  18. //写入对象
  19. public boolean setObject(final String key, Object value, Integer expireTime) {
  20. try {
  21. redisTemplate.opsForValue().set(key, value);
  22. redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
  23. return true;
  24. } catch (Exception e) {
  25. e.printStackTrace();
  26. return false;
  27. }
  28. }
  29. //获取对象
  30. public Object getObject(final String key) {
  31. return key == null ? null : redisTemplate.opsForValue().get(key);
  32. }
  33. //写入集合
  34. public boolean setList(final String key, Object value, Integer expireTime) {
  35. try {
  36. redisTemplate.opsForList().rightPush(key, value);
  37. redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
  38. return true;
  39. } catch (Exception e) {
  40. e.printStackTrace();
  41. return false;
  42. }
  43. }
  44. //获取集合
  45. public List<Object> getList(final String key) {
  46. try {
  47. return redisTemplate.opsForList().range(key, 0, -1);
  48. } catch (Exception e) {
  49. e.printStackTrace();
  50. return null;
  51. }
  52. }
  53. public boolean setHashAsKV(String key, Object hk, Object hv, Integer expireTime) {
  54. try {
  55. redisTemplate.opsForHash().put(key, hk, hv);
  56. redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
  57. return true;
  58. } catch (Exception e) {
  59. e.printStackTrace();
  60. }
  61. return false;
  62. }
  63. public boolean setHashAsMap(String key, Map map, Integer expireTime) {
  64. try {
  65. redisTemplate.opsForHash().putAll(key, map);
  66. redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
  67. return true;
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. }
  71. return false;
  72. }
  73. public Map getMap(String key) {
  74. Map<Object, Object> map = redisTemplate.opsForHash().entries(key);
  75. if (CollectionUtils.isEmpty(map)) {
  76. return null;
  77. }
  78. return map;
  79. }
  80. public Object getHashObject(String k1, String k2) {
  81. return redisTemplate.opsForHash().get(k1, k2);
  82. }
  83. /**
  84. * 判断是否存在key
  85. *
  86. * @param key key
  87. * @return
  88. */
  89. public boolean hasKey(final String key) {
  90. try {
  91. return redisTemplate.hasKey(key);
  92. } catch (Exception e) {
  93. e.printStackTrace();
  94. }
  95. return false;
  96. }
  97. /**
  98. * 删除key
  99. *
  100. * @param key key
  101. * @return
  102. */
  103. public void del(final String key) {
  104. if (hasKey(key)) {
  105. redisTemplate.delete(key);
  106. }
  107. }
  108. /**
  109. * 批量删除key
  110. *
  111. * @param keys keys
  112. * @return
  113. */
  114. public boolean delKeys(String... keys) {
  115. for (String key : keys) {
  116. if (hasKey(key)) {
  117. Boolean flag = redisTemplate.delete(key);
  118. if (flag == null) {
  119. continue;
  120. }
  121. if (!flag) {
  122. return false;
  123. }
  124. }
  125. }
  126. return true;
  127. }
  128. }

entity层

  1. package vip.xiaonuo.sys.modular.upgrade.entity;
  2. import lombok.Data;
  3. import org.springframework.web.multipart.MultipartFile;
  4. import java.io.Serializable;
  5. @Data
  6. public class MultipartFileParam implements Serializable {
  7. private static final long serialVersionUID = 3238600879053243080L;
  8. private String taskId;//文件传输任务ID
  9. private long chunkNumber;//当前为第几分片
  10. private long chunkSize;//每个分块的大小
  11. private long totalChunks;//分片总数
  12. private long fileSize;
  13. private String fileName;
  14. private String identifier;//文件唯一标识
  15. private MultipartFile file;//分块文件传输对象
  16. }
UpLoadConstant.java constant层
  1. package vip.xiaonuo.sys.modular.upgrade.constant;
  2. /**
  3. * @Author: geng
  4. * @Date: 2022/9/23 22:00
  5. * @Description:
  6. */
  7. public class UpLoadConstant {
  8. private final static String uploading = "Uploading:";
  9. private final static String file = uploading + "file:";
  10. //当前文件传输到第几块
  11. public final static String chunkNum = file + "chunkNum:";
  12. //当前文件上传到fastdfs路径
  13. public final static String fastDfsPath = file + "fastDfsPath:";
  14. public final static String task = uploading + "task:";
  15. public final static String fileMd5 = file + "md5:";
  16. }

param层

  1. package vip.xiaonuo.sys.modular.upgrade.param;
  2. /**
  3. * @Author: geng
  4. * @Date: 2022/9/23 22:27
  5. * @Description:
  6. */
  7. public class ApiResult {
  8. /**
  9. * 错误码.
  10. */
  11. private Integer code;
  12. /**
  13. * 提示信息.
  14. */
  15. private String msg;
  16. /**
  17. * 具体的内容.
  18. */
  19. private Object data;
  20. public Integer getCode() {
  21. return code;
  22. }
  23. public void setCode(Integer code) {
  24. this.code = code;
  25. }
  26. public String getMsg() {
  27. return msg;
  28. }
  29. public void setMsg(String msg) {
  30. this.msg = msg;
  31. }
  32. public Object getData() {
  33. return data;
  34. }
  35. public void setData(Object data) {
  36. this.data = data;
  37. }
  38. }
  1. package vip.xiaonuo.sys.modular.upgrade.param;
  2. public enum CustomResponse {
  3. SUCCESS(10000, "响应成功"),
  4. FAILURE(10001, "响应失败");
  5. private Integer code;
  6. private String msg;
  7. CustomResponse(Integer code, String msg) {
  8. this.code = code;
  9. this.msg = msg;
  10. }
  11. public Integer getCode() {
  12. return code;
  13. }
  14. public String getMsg() {
  15. return msg;
  16. }
  17. }
  1. package vip.xiaonuo.sys.modular.upgrade.param;
  2. /**
  3. * @Author: geng
  4. * @Date: 2022/9/23 22:28
  5. * @Description:
  6. */
  7. public class ResultUtil {
  8. public static ApiResult success(Object object) {
  9. ApiResult apiResult = new ApiResult();
  10. apiResult.setCode(CustomResponse.SUCCESS.getCode());
  11. apiResult.setMsg(CustomResponse.SUCCESS.getMsg());
  12. apiResult.setData(object);
  13. return apiResult;
  14. }
  15. public static ApiResult success() {
  16. ApiResult apiResult = new ApiResult();
  17. apiResult.setCode(CustomResponse.SUCCESS.getCode());
  18. apiResult.setMsg(CustomResponse.SUCCESS.getMsg());
  19. return apiResult;
  20. }
  21. public static ApiResult success(Integer code, String msg, Object obj) {
  22. ApiResult apiResult = new ApiResult();
  23. apiResult.setCode(code);
  24. apiResult.setMsg(msg);
  25. apiResult.setData(obj);
  26. return apiResult;
  27. }
  28. public static ApiResult error(Object object) {
  29. ApiResult apiResult = new ApiResult();
  30. apiResult.setCode(CustomResponse.FAILURE.getCode());
  31. apiResult.setMsg(CustomResponse.FAILURE.getMsg());
  32. apiResult.setData(object);
  33. return apiResult;
  34. }
  35. public static ApiResult errMsg(String msg) {
  36. ApiResult apiResult = new ApiResult();
  37. apiResult.setCode(CustomResponse.FAILURE.getCode());
  38. apiResult.setMsg(msg);
  39. return apiResult;
  40. }
  41. public static ApiResult error() {
  42. ApiResult apiResult = new ApiResult();
  43. apiResult.setCode(CustomResponse.FAILURE.getCode());
  44. apiResult.setMsg(CustomResponse.FAILURE.getMsg());
  45. return apiResult;
  46. }
  47. public static ApiResult error(Integer code, String msg, Object obj) {
  48. ApiResult apiResult = new ApiResult();
  49. apiResult.setCode(code);
  50. apiResult.setMsg(msg);
  51. apiResult.setData(obj);
  52. return apiResult;
  53. }
  54. }

controller层

  1. @GetMapping("/checkMd5")
  2. public ApiResult checkMd5(@Param ("md5") String md5) {
  3. Map<String, Object> map = uploadFileUtil.checkMd5(md5);
  4. return ResultUtil.success(map);
  5. }
  6. @PostMapping(value = "/chunkUpload")
  7. public ApiResult chunkUpload(MultipartFileParam multipartFileParam) {
  8. return uploadFileUtil.uploadAppendFile(multipartFileParam, "D:\\klp");
  9. }

前端代码

  1. chunkUpload() {
  2. const file = this.fileList[0]
  3. const md5 = file.name + file.size + file.lastModified
  4. chunkUploaMess(md5).then((res)=>{
  5. if (res) {
  6. console.log(res)
  7. const start = Number(res.data.fileSize)
  8. const taskId = res.data.taskId
  9. if (res.data) {
  10. this.upload(start, taskId, file)
  11. } else {
  12. this.upload(0, taskId, file)
  13. }
  14. })
  15. }
  1. upload(start, taskId, file) {
  2. // 分片大小 5M
  3. const bytePercent = 1024 * 1024 * 5
  4. // 通过文件大小除以分片大小得出总片数
  5. const totalChunks = Math.ceil(file.size / bytePercent)
  6. // 起始位置+分片数 如果大于文件大小,那么终点位置就是文件大小,反之就是前者
  7. const end = (start + bytePercent) > file.size ? file.size : (start + bytePercent)
  8. const fileName = file.name
  9. // 分片文件
  10. const chunkFile = file.slice(start, end)
  11. // 当前分片数
  12. const currChunkNum =parseInt (start / bytePercent) + 1
  13. const formData = new FormData()
  14. formData.append('file', chunkFile)
  15. formData.append('fileName', fileName)
  16. formData.append('fileSize', start)
  17. formData.append('taskId', taskId)
  18. formData.append('chunkNumber', currChunkNum)
  19. formData.append('chunkSize', bytePercent)
  20. formData.append('totalChunks', totalChunks)
  21. uploadsMess(formData).then((res)=>{
  22. if (res.data.result === '上传完毕') {
  23. alert('成功')
  24. } else {
  25. this.upload(end, taskId, file)
  26. }
  27. })
  28. },

api

  1. /**
  2. * 分片
  3. * @returns {AxiosPromise}
  4. * @param md5
  5. */
  6. export function chunkUpload(md5) {
  7. return axios({
  8. url: '/main/checkMd5',
  9. method: 'get',
  10. data: md5,
  11. })
  12. }
  13. /**
  14. * 分片
  15. * @returns {AxiosPromise}
  16. * @param formData
  17. */
  18. export function uploads(formData) {
  19. return axios({
  20. url: '/main/chunkUpload',
  21. method: 'POST',
  22. data: formData,
  23. contentType: false,//很重要,指定为false才能形成正确的Content-Type
  24. processData: false, //加入这属性 processData默认为true,为true时,提交不会序列化data。
  25. })
  26. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小丑西瓜9/article/detail/170029
推荐阅读
相关标签
  

闽ICP备14008679号