【SpringBoot】文件分片上传、合并

背景

在上传大型文件时,一般采用的都是分片、断点续传等技术,这样不会导致因文件过大而造成系统超时或者过压等情况。

 接下来我们进入教学

如果有帮助到您,麻烦请点击个收藏、赞,谢谢~

一、实际效果图

整个前端网页的效果图:

二、后端代码

1.开发环境

开发工具:idea  ,开发语言:java  ,框架:springboot

2.创建文件上传工具类

创建工具类:FileUtils


import com.caozhen.common.constant.HttpStatusConstant;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;import java.io.*;/*** @author caozhen* @ClassName FileUtils* @description: 文件上传* @date 2023年10月07日* @version: 1.0*/
@Component
public class FileUtils {//文件路径,可以统一配置private static final String UPLOAD_DIR = "d:\\uploads";/**** 断点续传(上传文件)* @param file* @return*/public ResultAjax resumeFile(MultipartFile file,int chunkNumber,int totalChunks,String identifier) {try {File uploadDir = new File(UPLOAD_DIR);if (!uploadDir.exists()) {uploadDir.mkdirs();}String fileName = identifier + "-chunk-" + chunkNumber;File chunkFile = new File(uploadDir, fileName);try (InputStream inputStream = file.getInputStream();FileOutputStream outputStream = new FileOutputStream(chunkFile)) {byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}}if (chunkNumber == totalChunks - 1) {File finalFile = new File(UPLOAD_DIR + File.separator + identifier);for (int i = 0; i < totalChunks; i++) {File part = new File(uploadDir, identifier + "-chunk-" + i);try (FileInputStream partStream = new FileInputStream(part);FileOutputStream finalStream = new FileOutputStream(finalFile, true)) {byte[] buffer = new byte[1024];int bytesRead;while ((bytesRead = partStream.read(buffer)) != -1) {finalStream.write(buffer, 0, bytesRead);}}part.delete();}uploadDir.delete();}return ResultAjax.ok("Chunk " + chunkNumber + " uploaded");} catch (IOException e) {e.printStackTrace();return ResultAjax.error(HttpStatusConstant.HTTP_CODE_500, "上传失败:" + e.getMessage());}}
}

3.创建controller类

@RestController
@Slf4j
@Api(tags = "系统服务")
@RequestMapping("/sys/test/")
public class SystemController {@Autowiredprivate FileUtils fileUtils;@ApiOperation("测试文件断点续传-上传")@RequestMapping(value = "/upload", method = RequestMethod.POST)public ResultAjax fileUploads(@RequestParam("file") MultipartFile file,@RequestParam("chunkNumber") int chunkNumber,@RequestParam("totalChunks") int totalChunks,@RequestParam("identifier") String identifier) {return fileUtils.resumeFile(file,chunkNumber,totalChunks,identifier);}}

4.自定义封装类ResultAjax


import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;/*** @author caozhen* @ClassName ResultAjax* @description: 全局封装统一返回实体类* @date 2023年07月26日* @version: 1.0*/
public class ResultAjax extends LinkedHashMap<String, Object> {private static final long serialVersionUID = 1L;public ResultAjax() {put("success", true);put("code", HttpStatusConstant.HTTP_CODE_200);put("msg", "操作成功!");put("data", null);}public ResultAjax(Integer code) {put("success", true);put("code", code);put("msg", "操作成功!");put("data", null);}public ResultAjax(Integer code, String msg) {put("success", true);put("code", code);put("msg", msg);put("data", null);}public static ResultAjax error() {return error(HttpStatusConstant.HTTP_CODE_500, "未知异常,请联系管理员");}public static ResultAjax errorDebug(String message) {return error(HttpStatusConstant.HTTP_CODE_500, "未知异常 " + message + ",请联系管理员");}public static ResultAjax error(String msg) {return error(HttpStatusConstant.HTTP_CODE_500, msg);}public static ResultAjax error(int code, String msg) {ResultAjax r = new ResultAjax();r.put("success", false);r.put("code", code);r.put("msg", msg);return r;}public ResultAjax errorInfo(String msg) {this.put("errorMsg", msg);return this;}public static ResultAjax ok(Object data) {ResultAjax r = new ResultAjax();r.put("success", true);r.put("code", HttpStatusConstant.HTTP_CODE_200);r.put("msg", "操作成功!");r.put("data", data);return r;}public static ResultAjax ok(Map<String, Object> map) {ResultAjax r = new ResultAjax();r.putAll(map);r.put("data", null);return r;}public static ResultAjax ok(String msg, Integer code) {ResultAjax r = new ResultAjax();r.put("success", true);r.put("code", code);r.put("msg", msg);r.put("data", null);return r;}public static ResultAjax ok() {return new ResultAjax().put("msg", "操作成功!").put("data", null);}public static ResultAjax ok(Integer size) {return new ResultAjax().put("data", null);}@Overridepublic ResultAjax put(String key, Object value) {super.put(key, value);return this;}/*** 添加返回结果数据** @param key* @param value* @return*/public ResultAjax putData(String key, Object value) {Map<String, Object> map = (HashMap<String, Object>) this.get("data");map.put(key, value);return this;}
}
public class HttpStatusConstant {//目前常用的,200,500public final static Integer HTTP_CODE_200 = 200;public final static Integer HTTP_CODE_500 = 500;//拦截public final static Integer HTTP_CODE_403 = 403;public final static Integer HTTP_CODE_401 = 401;
}

三、前端代码

<!DOCTYPE html>
<html>
<head><title>文件分片上传</title>
</head>
<body><input type="file" id="fileInput"><button id="startUpload">开始上传</button><button id="pauseResumeUpload">暂停上传</button><progress id="progress" max="100" value="0"></progress><span id="progressText">0%</span><script>const fileInput = document.getElementById('fileInput');const startUpload = document.getElementById('startUpload');const pauseResumeUpload = document.getElementById('pauseResumeUpload');const progress = document.getElementById('progress');const progressText = document.getElementById('progressText');let file;let uploading = false;let currentChunk = 0;let uploadPaused = false;let xhr;fileInput.addEventListener('change', (event) => {file = event.target.files[0];});async function uploadFile() {const chunkSize = 1 * 1024 * 1024; // 1MBconst totalChunks = Math.ceil(file.size / chunkSize);for (currentChunk; currentChunk < totalChunks; currentChunk++) {if (uploadPaused) {break;}const startByte = currentChunk * chunkSize;const endByte = Math.min(startByte + chunkSize, file.size);const chunk = file.slice(startByte, endByte);const formData = new FormData();formData.append('file', chunk);formData.append('chunkNumber', currentChunk);formData.append('totalChunks', totalChunks);formData.append('identifier', file.name);const response = await fetch('http://localhost:8000/system/sys/test/upload', {method: 'POST',body: formData,});if (response.status !== 200) {console.error('Error uploading chunk: ', response.statusText);break;}const percent = ((currentChunk + 1) / totalChunks) * 100;progress.value = percent;progressText.textContent = `${Math.round(percent)}%`;}if (currentChunk >= totalChunks) {alert('文件上传成功!');uploading = false;currentChunk = 0;progress.value = 100;progressText.textContent = '100%';}}startUpload.addEventListener('click', () => {if (file && !uploading) {uploading = true;uploadFile();}});pauseResumeUpload.addEventListener('click', () => {if (uploading) {if (!uploadPaused) {uploadPaused = true;pauseResumeUpload.textContent = '继续上传';} else {uploadPaused = false;pauseResumeUpload.textContent = '暂停上传';uploadFile();}}});</script>
</body>
</html>

四、接下来我们演示下

五、留言区

有什么问题可以在底下评论区留言,看到会在本文后追加回复!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/128308.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

模块化编程+LCD1602调试工具——“51单片机”

各位CSDN的uu们你们好呀&#xff0c;小雅兰又来啦&#xff0c;刚刚学完静态数码管显示和动态数码管显示&#xff0c;感觉真不错呢&#xff0c;下面&#xff0c;小雅兰就要开始学习模块化编程以及LCD1602调试工具的知识了&#xff0c;让我们进入51单片机的世界吧&#xff01;&am…

37.普利姆(Prim)算法

从一个问题开始 “要想富&#xff0c;先修路”&#xff0c;郝乡长最近为了德胜乡修路的事情愁白了头。 得胜乡有A、B、C、D、E、F、G七个村子&#xff0c;现在需要修路把7个村庄连通&#xff0c;但是又想要耗费的公路建材最少&#xff08;修建公路的总里程最短&#xff09;&…

【Linux】TCP的服务端(守护进程) + 客户端

文章目录 &#x1f4d6; 前言1. 服务端基本结构1.1 类成员变量&#xff1a;1.2 头文件1.3 初始化&#xff1a;1.3 - 1 全双工与半双工1.3 - 2 inet_aton1.3 - 3 listen 2. 服务端运行接口2.1 accept&#xff1a;2.2 服务接口&#xff1a; 3. 客户端3.1 connect&#xff1a;3.2 …

条件查询和数据查询

一、后端 1.controller层 package com.like.controller;import com.like.common.CommonDto; import com.like.entity.User; import com.like.service.UserService; import jakarta.annotation.Resource; import org.springframework.web.bind.annotation.GetMapping; import …

文心一言 VS 讯飞星火 VS chatgpt (108)-- 算法导论10.1 6题

六、用go语言&#xff0c;说明如何用两个栈实现一个队列&#xff0c;并分析相关队列操作的运行时间。 文心一言&#xff1a; 使用两个栈实现队列的基本思想是利用一个栈&#xff08;stack1&#xff09;来处理入队操作&#xff0c;另一个栈&#xff08;stack2&#xff09;来处…

ELK集群 日志中心集群

ES&#xff1a;用来日志存储 Logstash:用来日志的搜集&#xff0c;进行日志格式转换并且传送给别人&#xff08;转发&#xff09; Kibana:主要用于日志的展示和分析 kafka Filebeat:搜集文件数据 es-1 本地解析 vi /etc/hosts scp /etc/hosts es-2:/etc/hosts scp /etc…

手机切换ip地址的几种方法详解

在某些情况下&#xff0c;我们可能需要切换手机的IP地址来实现一些特定的需求&#xff0c;如解决某些应用程序的限制、绕过IP封禁等。本文将为大家分享几种切换手机IP地址的方法&#xff0c;让您能够轻松应对各种需求。 一、使用动态服务器 使用动态服务器是一种常见的切换手机…

【Leetcode】 51. N 皇后

按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案。 每一种…

分布式文件系统HDFS(林子雨慕课课程)

文章目录 3. 分布式文件系统HDFS3.1 分布式文件系统HDFS简介3.2 HDFS相关概念3.3 HDFS的体系结构3.4 HDFS的存储原理3.5 HDFS数据读写3.5.1 HDFS的读数据过程3.5.2 HDFS的写数据过程 3.6 HDFS编程实战 3. 分布式文件系统HDFS 3.1 分布式文件系统HDFS简介 HDFS就是解决海量数据…

基于springboot实现人职匹配推荐管理系统演示【项目源码+论文说明】分享

基于springboot实现人职匹配推荐管理系统演示 摘要 随着科学技术的飞速发展&#xff0c;各行各业都在努力与现代先进技术接轨&#xff0c;通过科技手段提高自身的优势&#xff1b;对于人职匹配推荐系统当然也不能排除在外&#xff0c;随着网络技术的不断成熟&#xff0c;带动了…

合宙Air780e+luatos+腾讯云物联网平台完成设备通信与控制(属性上报+4G远程点灯)

1.腾讯云物联网平台 首先需要在腾讯云物联网平台创建产品、创建设备、定义设备属性和行为&#xff0c;例如&#xff1a; &#xff08;1&#xff09;创建产品 &#xff08;2&#xff09;定义设备属性和行为 &#xff08;3&#xff09;创建设备 &#xff08;4&#xff09;准备参…

【Zookeeper专题】Zookeeper经典应用场景实战(一)

目录 前置知识课程内容一、Zookeeper Java客户端实战1.1 Zookeeper 原生Java客户端使用1.2 Curator开源客户端使用快速开始使用示例 二、Zookeeper在分布式命名服务中的实战2.1 分布式API目录2.2 分布式节点的命名2.3 分布式的ID生成器 三、zookeeper实现分布式队列3.1 设计思路…