1. 前端(vue element ui & 原生)

初始变量声明:

  1. currentFile: {}, // 当前上传的文件
  2. bigFileSliceCount: 20, // 大文件切片后的子文件数量(也可使用其它限定方式,如按照文件大小,每10MB切一片,此处采用的是固定切片的子文件数量的方式倒推切片大小)

  

接口:切片上传图片&合并切片文件

  1. <el-button @click="partUploadClick()" type="info"> 上传文件</el-button>
  2. <input v-show="false" id="uploadPartfile" class="upload-css" type='file' @click.stop=''
  3. @change='handleFileChange($event, currentFile); currentFile = {}' single />

  

  1. // 文件上传处理
  2. partUploadClick() {
  3. var clickEvent = document.createEvent('MouseEvent'); // 1.创建一个鼠标事件类型
  4. clickEvent.initMouseEvent('click', false, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null); // 2.初始化一个click事件
  5. document.getElementById('uploadPartfile').dispatchEvent(clickEvent); // 3.派发(触发)
  6. },

  

  1. handleFileChange(event, item) {
  2. let inputDom = event.target // input 本身,从这里获取 files<FileList>
  3. let files = inputDom.files // input 中的文件,是 FileList 对象,一个类似数组的文件组,但不是数组,可遍历
  4. var GUID = this.guid();
  5. var file = files[0], //文件对象
  6. name = file.name, //文件名
  7. size = file.size; //总大小
  8. // console.log(size)
  9. if (size <= 524288000) { // 文件大小小于等于500MB,直接整文件上传
  10. this.handleFiles(event, item)
  11. } else { // 大于500MB 切片上传
  12. var shardSize = size / this.bigFileSliceCount, // 根据切片数量,确定每切片的大小是多少
  13. shardCount = Math.ceil(size / shardSize); //总片数
  14. item.download = true
  15. for (var i = 0; i < shardCount; ++i) {
  16. //计算每一片的起始与结束位置
  17. var start = i * shardSize,
  18. end = Math.min(size, start + shardSize);
  19. var partFile = file.slice(start, end);
  20. this.partUpload(GUID, partFile, name, shardCount, i, item.fullPath, item, size);
  21. }
  22. }
  23. },

  

  1. partUpload: function (GUID, partFile, name, chunks, chunk, filePath, item, size) {
  2. //构造一个表单,FormData是HTML5新增的
  3. const _this = this
  4. var form = new FormData();
  5. form.append("guid", GUID);
  6. form.append("file", partFile); //slice方法用于切出文件的一部分
  7. form.append("fileName", name);
  8. form.append("chunks", chunks); //总片数
  9. form.append("chunk", chunk); //当前是第几片
  10. form.append("filePath", filePath); //文件保存路径
  11. uploadFileByPart(form).then((res) => {
  12. // console.log(res)
  13. let data = res.data
  14. _this.status++;
  15. if (data.code == 200) {
  16. _this.complete = this.status * (100 / this.bigFileSliceCount)
  17. let finishSize = this.status != this.bigFileSliceCount ? (size / this.bigFileSliceCount) * this.status : size
  18. if (finishSize > 1048576) {
  19. _this.finishSize = parseFloat(finishSize / 1048576).toFixed(1) + 'M';
  20. } else if (finishSize > 1024) {
  21. _this.finishSize = parseInt(finishSize / 1024) + 'K';
  22. } else {
  23. _this.finishSize = finishSize + 'B';
  24. }
  25. // console.log(this.status + " / " + chunks)
  26. }
  27. if (this.status == chunks) {
  28. _this.mergeFile(GUID, name, filePath, item);
  29. }
  30. }).catch((err) => {
  31. console.log("请求出错", err);
  32. });
  33. },

  

  1. // 切片上传文件
  2. export const uploadFileByPart = (fileFormData) => {
  3. return http({
  4. url: `/xxxxx/files/part`,
  5. method: 'post',
  6. data: fileFormData,
  7. async: true,
  8. processData: false,
  9. contentType: false
  10. })
  11. }

  

  1. mergeFile: function (GUID, name, filePath, item) {
  2. var formMerge = new FormData();
  3. formMerge.append("guid", GUID);
  4. formMerge.append("fileName", name);
  5. formMerge.append("filePath", filePath);
  6. mergeFileByPart(formMerge).then((res) => {
  7. item.download = false
  8. let data = res.data
  9. if (data.code == 200) {
  10. this.$message({
  11. message: '上传成功',
  12. type: 'success'
  13. });
  14. this.getFalcoPath();
  15. }
  16. }).catch((err) => {
  17. item.download = false
  18. console.log("请求出错", err);
  19. });
  20. },

  

  1. // 切片合并文件
  2. export const mergeFileByPart = (fileFormData) => {
  3. return http({
  4. url: `/xxxxx/files/merge`,
  5. method: 'post',
  6. processData: false,
  7. contentType: false,
  8. data: fileFormData
  9. })
  10. }

  

  1. guid: function (prefix) {
  2. var counter = 0;
  3. var guid = (+new Date()).toString(32),
  4. i = 0;
  5. for (; i < this.bigFileSliceCount; i++) {
  6. guid += Math.floor(Math.random() * 65535).toString(32);
  7. }
  8. return (prefix || 'wu_') + guid + (counter++).toString(32);
  9. }

  

  1. // 整文件上传时的处理
  2. handleFiles(event, item) {
  3. let inputDom = event.target // input 本身,从这里获取 files<FileList>
  4. let files = inputDom.files // input 中的文件,是 FileList 对象,一个类似数组的文件组,但不是数组,可遍历
  5. // console.log(files)
  6. let fileFormData = new window.FormData()
  7. fileFormData.append('file', files[0])
  8. item.download = true
  9. // console.log("上传文件路径" + item.fullPath)
  10. const _this = this
  11. uploadFileByPath(fileFormData, item.fullPath, this.gress).then((res) => {
  12. // console.log(res)
  13. item.download = false
  14. inputDom.value = ''
  15. if (res.data.code == 200) {
  16. _this.$message({
  17. message: '上传成功',
  18. type: 'success'
  19. });
  20. this.getFalcoPath();
  21. } else if (res.data.code == 2001) {
  22. this.$message.error('上传失败');
  23. }
  24. }).catch((err) => {
  25. item.download = false
  26. console.log("请求出错", err);
  27. });
  28. },

  

  1. // 单个文件完整上传文件
  2. export const uploadFileByPath = (fileFormData, filePath, uploadProgress) => {
  3. return http({
  4. url: `/xxxxx/files/upload`,
  5. method: 'post',
  6. onUploadProgress: function (progressEvent) {
  7. uploadProgress(progressEvent)
  8. },
  9. data: fileFormData,
  10. params: { 'filePath': filePath }
  11. })
  12. }

  

  1. // 上传与下载文件的回调
  2. gress(progress) {
  3. const self = this
  4. this.complete = ((progress.loaded / progress.total) * 100).toFixed(0)
  5.  
  6. if (progress.loaded > 1048576) {
  7. self.finishSize = parseFloat(progress.loaded / 1048576).toFixed(1) + 'M';
  8. } else if (progress.loaded > 1024) {
  9. self.finishSize = parseInt(progress.loaded / 1024) + 'K';
  10. } else {
  11. self.finishSize = progress.loaded + 'B';
  12. }
  13. // console.log("已下载:" + self.finishSize + ' 比例 ' + this.complete)
  14. },

  

2. 后台

接口:

  1. @ResponseBody
  2. @PostMapping("/xxxxx/files/upload")
  3. public ApiResult upload(@RequestParam(value = "file", required = false) MultipartFile multipartFile, @RequestParam(required = false) String filePath) {
  4. File file=new File(filePath + File.separator + multipartFile.getOriginalFilename());
  5. try {
  6. FileUtil.copy(multipartFile.getBytes(), file);
  7. } catch (IOException e) {
  8. return ApiResult.fail(2001,"上传失败");
  9. }
  10. return ApiResult.ok("上传成功");
  11. }

  12. @PostMapping("/xxxxx/files/part")
  13. @ResponseBody
  14. public ApiResult bigFile(HttpServletRequest request, HttpServletResponse response, String guid, Integer chunk, MultipartFile file, Integer chunks, String filePath, String fileName) {
  15. try {
  16. boolean isMultipart = ServletFileUpload.isMultipartContent(request);
  17. if (isMultipart) {
  18. if (chunk == null) chunk = 0;
  19. // 临时目录用来存放所有分片文件
  20. String tempFileDir = applicationProperties.getNasPath() + File.separator + "临时文件夹" + File.separator + guid;
  21. File parentFileDir = new File(tempFileDir);
  22. if (!parentFileDir.exists()) {
  23. parentFileDir.mkdirs();
  24. }
  25. // 分片处理时,前台会多次调用上传接口,每次都会上传文件的一部分到后台
  26. log.info(SecurityUtils.getCurrentLogin() + " 上传:" + filePath + File.separator + fileName + "; 切片文件名:" + guid + "_" + chunk + ".part");
  27. File tempPartFile = new File(parentFileDir, guid + "_" + chunk + ".part");
  28. FileUtils.copyInputStreamToFile(file.getInputStream(), tempPartFile);
  29. }
  30.  
  31. } catch (Exception e) {
  32. log.error(SecurityUtils.getCurrentLogin() + " 上传:" + filePath + File.separator + fileName + "; 切片文件名:" + guid + "_" + chunk + ".part" + " error: " + e.getMessage());
  33. e.printStackTrace();
  34. return ApiResult.fail(2009,e.getMessage());
  35. }
  36. return ApiResult.ok(200,"上次成功");
  37. }

  38. @RequestMapping("/xxxxx/files/merge")
  39. @ResponseBody
  40. public ApiResult mergeFile(String guid, String fileName, String filePath) {
  41. try {
  42. log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " start");
  43. String sname = fileName.substring(fileName.lastIndexOf("."));
  44. //时间格式化格式
  45. Date currentTime = new Date();
  46. SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmssSSS");
  47. //获取当前时间并作为时间戳
  48. String timeStamp = simpleDateFormat.format(currentTime);
  49. //拼接新的文件名
  50. String newName = timeStamp + sname;
  51. simpleDateFormat = new SimpleDateFormat("yyyyMM");
  52. String path = applicationProperties.getNasPath() + File.separator + "临时文件夹" + File.separator ;
  53. String tmp = simpleDateFormat.format(currentTime);
  54. File parentFileDir = new File(path + guid);
  55. if (parentFileDir.isDirectory()) {
  56. File destTempFile = new File(filePath, fileName);
  57. if (!destTempFile.exists()) {
  58. //先得到文件的上级目录,并创建上级目录,在创建文件
  59. destTempFile.getParentFile().mkdir();
  60. try {
  61. destTempFile.createNewFile();
  62. } catch (IOException e) {
  63. e.printStackTrace();
  64. }
  65. }
  66. for (int i = 0; i < parentFileDir.listFiles().length; i++) {
  67. File partFile = new File(parentFileDir, guid + "_" + i + ".part");
  68. FileOutputStream destTempfos = new FileOutputStream(destTempFile, true);
  69. //遍历"所有分片文件"到"最终文件"中
  70. FileUtils.copyFile(partFile, destTempfos);
  71. destTempfos.close();
  72. }
  73. // 删除临时目录中的分片文件
  74. FileUtils.deleteDirectory(parentFileDir);
  75. log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " end ");
  76. return ApiResult.ok(200,"合并成功");
  77. }else{
  78. log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " end error: 没找到目录");
  79. return ApiResult.fail(2007,"没找到目录");
  80. }
  81.  
  82. } catch (Exception e) {
  83. log.info(SecurityUtils.getCurrentLogin() + " 合并切片文件:" + filePath + File.separator + fileName + " end error: " + e.getMessage());
  84. e.printStackTrace();
  85. return ApiResult.fail(2008,e.getMessage());
  86. }
  87.  
  88. }

工具类:

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.springframework.util.Assert;
  3. import org.springframework.util.StreamUtils;
  4.  
  5. import java.io.*;
  6. import java.nio.file.*;
  7.  
  8. @Slf4j
  9. public class FileUtil {
  10.  
  11. public static void copy(byte[] in, File out) throws IOException {
  12. Assert.notNull(in, "No input byte array specified");
  13. Assert.notNull(out, "No output File specified");
  14. copy(new ByteArrayInputStream(in), Files.newOutputStream(out.toPath()));
  15. }
  16.  
  17. public static int copy(InputStream in, OutputStream out) throws IOException {
  18. Assert.notNull(in, "No InputStream specified");
  19. Assert.notNull(out, "No OutputStream specified");
  20.  
  21. try {
  22. return StreamUtils.copy(in, out);
  23. } finally {
  24. try {
  25. in.close();
  26. } catch (IOException ex) {
  27. }
  28. try {
  29. out.close();
  30. } catch (IOException ex) {
  31. }
  32. }
  33. }
  34.  
  35. }

相关引用Class:

  1. import org.apache.commons.io.FileUtils;

  

springboot 大文件切片上传的更多相关文章

  1. java springboot 大文件分片上传处理

    参考自:https://blog.csdn.net/u014150463/article/details/74044467 这里只写后端的代码,基本的思想就是,前端将文件分片,然后每次访问上传接口的时 ...

  2. 利用blob对象实现大文件分片上传

    首先说分片上传,我们在进行文件上传的时候,因为服务器的限制,会限制每一次上传到服务器的文件大小不会很大,这个时候我们就需要把一个需要上传的文件进行切割,然后分别进行上传到服务器. 假如需要做到这一步, ...

  3. iOS大文件分片上传和断点续传

    总结一下大文件分片上传和断点续传的问题.因为文件过大(比如1G以上),必须要考虑上传过程网络中断的情况.http的网络请求中本身就已经具备了分片上传功能,当传输的文件比较大时,http协议自动会将文件 ...

  4. js实现大文件分片上传的方法

    借助js的Blob对象FormData对象可以实现大文件分片上传的功能,关于Blob和FormData的具体使用方法可以到如下地址去查看FormData 对象的使用Blob 对象的使用以下是实现代码, ...

  5. Node + js实现大文件分片上传基本原理及实践(一)

    _ 阅读目录 一:什么是分片上传? 二:理解Blob对象中的slice方法对文件进行分割及其他知识点 三. 使用 spark-md5 生成 md5文件 四. 使用koa+js实现大文件分片上传实践 回 ...

  6. 视频大文件分片上传(使用webuploader插件)

    背景 公司做网盘系统,一直在调用图片服务器的接口上传图片,以前写的,以为简单改一改就可以用 最初要求 php 上传多种视频格式,支持大文件,并可以封面截图,时长统计 问题 1.上传到阿里云服务器,13 ...

  7. vue大文件分片上传插件

    最近遇见一个需要上传百兆大文件的需求,调研了七牛和腾讯云的切片分段上传功能,因此在此整理前端大文件上传相关功能的实现. 在某些业务中,大文件上传是一个比较重要的交互场景,如上传入库比较大的Excel表 ...

  8. nodeJs + js 大文件分片上传

    简单的文件上传 一.准备文件上传的条件: 1.安装nodejs环境 2.安装vue环境 3.验证环境是否安装成功 二.实现上传步骤 1.前端部分使用 vue-cli 脚手架,搭建一个 demo 版本, ...

  9. PHP实现大文件的上传设置

    打开php.ini,首先找到 ;;;;;;;;;;;;;;;; ; File Uploads ; ;;;;;;;;;;;;;;;; 区域,有影响文件上传的以下几个参数: file_uploads = ...

  10. Webuploader 大文件分片上传

    百度Webuploader 大文件分片上传(.net接收)   前阵子要做个大文件上传的功能,找来找去发现Webuploader还不错,关于她的介绍我就不再赘述. 动手前,在园子里找到了一篇不错的分片 ...

随机推荐

  1. debian11安装备忘

    1. 网卡驱动 参考网址:如何安装Debian RTL8821CE驱动? 2. 分辨率 貌似还是有点问题,还要进一步研究一下 参考网址:虚拟机中debian11修改控制台(console)分辨率|li ...

  2. 解决方案 | onenote无法同步,显示:证书错误,应用程序在加载SSL库是遇到内部错误。

    解决方案:一般是公司网络或者学校网络的问题,更换手机使用的数据流量热点无线网络即可.

  3. 【原创软件】第2期:CAD文字快速批量替换工具CFR(CAD_FastReplace_V4)

    01 背景 由于工作需要,开发了一套CAD文字快速批量替换软件CFR.主要目的是:实现dwg文件一次性完成单对/多对词组快速批量替换. 02 主要功能特色 (1)无需打开CAD,快速实现文字批量替换. ...

  4. 2024秋招西山居游戏开发SEED种子实习笔试题

    西山居游戏开发SEED种子实习 2024年秋招笔试题目,仅供参考,请大佬多多指教 选择题 逆波兰数,TCP,操作系统FIFO,C语言大小端 填空题 一道LUA脚本写结果,一道并发存储优化题,计算机系统 ...

  5. webgl未使用独立显卡报告2

    楔子 在上一篇文章 <# [https://juejin.cn/post/7074771064286347301] webgl未使用独立显卡报告> 发表后,有读者在公众号给我发了一段评论, ...

  6. 深入解读RabbitMQ工作原理

    RabbitMQ简介 在介绍RabbitMQ之前首先要介绍一下MQ,MQ是什么?MQ全称是Message Queue,可以理解为消息队列的意思. RabbitMQ是一个实现了AMQP(Advanced ...

  7. Memcache 与 Memcached 的区别

    Memcached 从0.2.0开始,要求PHP版本>=5.2.0,Memcache 要求PHP版本>=4.3. Memcached 最后发布时间为2018-12-24,Memcache ...

  8. [oeasy]python0133_[趣味拓展]好玩的unicode字符_另类字符_上下颠倒英文字符

    另类字符 回忆上次内容 上次再次输出了大红心<span style="font-size:64px;color:red"></span> 找到了红心对应的编 ...

  9. [oeasy]python0051_ 转义_escape_字符_character_单引号_双引号_反引号_ 退格键

    转义字符 回忆上次内容 上次研究的是进制转化 10进制可以转化为其他形式 bin oct hex 其他进制也可以转化为10进制 int 可以设置base来决定转为多少进制 回忆一下 我们为什么会有八进 ...

  10. ABC357-C题解

    最近一直掉分,谔谔. 分析 发现机房里面除了我以外都用递归写的,那我就来讲一种非递归的吧. 考虑第 \(i\) 级地毯拆成九块以后其实就是八块第 \(i-1\) 级地毯与一块大小为 \(3^{i-1} ...