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

news2024/7/4 4:33:07

背景

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

 接下来我们进入教学

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

一、实际效果图

整个前端网页的效果图:

二、后端代码

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 {
  
    @Autowired
    private 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);
    }

    @Override
    public 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,500
    public 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; // 1MB
            const 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.coloradmin.cn/o/1066649.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

商城小程序代客下单程序开发演示

一款专为传统电商、实体商家开发的商城系统小程序&#xff0c;做私域、做留存、做社交必备功能全都有。 1、丰富的营销玩法&#xff1a;拼团、秒杀、定金预售、分销、社区团购、积分商城、支付有礼等主流获客玩法都有。 2、强大的会员体系&#xff1a;普通会员、付费会员、会…

spring 事务源码阅读

开启事务 使用EnableTransactionManagement注解开启事务 该注解会引入TransactionManagementConfigurationSelector类&#xff0c;然后该类导入两个类AutoProxyRegistrar和ProxyTransactionManagementConfiguration。 1、添加bean后置处理器 AutoProxyRegistrar类的作用是注…

883. 高斯消元解线性方程组

883. 高斯消元解线性方程组 - AcWing题库 输入一个包含 n 个方程 n 个未知数的线性方程组。 方程组中的系数为实数。 求解这个方程组。 下图为一个包含 m 个方程 n 个未知数的线性方程组示例&#xff1a; 输入格式 第一行包含整数 n。 接下来 n 行&#xff0c;每行包含 n1…

千万不要支付赎金!解密.halo勒索病毒的秘诀在这里

导言&#xff1a; .halo 勒索病毒等勒索病毒已经成为网络犯罪分子的利器&#xff0c;威胁着很多企业的数据安全。本文91数据恢复将为您介绍 .halo 勒索病毒的新面貌&#xff0c;以及一些创新的方法&#xff0c;如何保护和恢复被 .halo 勒索病毒加密的数据文件&#xff0c;并提供…

【计算机视觉|人脸建模】学习从图像中回归3D面部形状和表情而无需3D监督

本系列博文为深度学习/计算机视觉论文笔记&#xff0c;转载请注明出处 标题&#xff1a;Learning to Regress 3D Face Shape and Expression from an Image without 3D Supervision 链接&#xff1a;[1905.06817] Learning to Regress 3D Face Shape and Expression from an I…

6-10 单链表分段逆转 分数 15

void K_Reverse( List L, int K ) { //此题已经默认size > K 因为当size < K时 反转后将不再符合链表的定义//求出表中元素个数int size 0;for (List cur L->Next; cur ! NULL; cur cur->Next)size; List prv, cur, next, first, head L;//共需要反转 si…

功率放大器可以放大电压吗为什么

功率放大器是一种电子设备&#xff0c;用于将输入信号的功率放大到更高的水平。而电压是功率的一个组成部分&#xff0c;所以功率放大器可以放大电压。 在功率放大器中&#xff0c;输入信号经过放大器的放大阶段后&#xff0c;会得到一个更强的输出信号。这个放大过程是通过增加…

HTML5开发实例-3D全景(ThreeJs全景Demo) 详解(图)

前言 在现在市面上很多全景H5的环境下,要实现全景的方式有很多,可以用css3直接构建也可以用基于threeJs的库来实现,还有很多别的制作全景的软件使用 本教学适用于未开发过3D全景的工程狮 如果觉得内容太无聊可以直接跳到最后 下载代码 理论 整个3D全景所用的相关理论就…

CSS布局 | flex布局

flex布局 flex是CSS3中新增的布局手段&#xff0c;优势是适用于不同屏幕尺寸和设备&#xff0c;当布局涉及到不定宽度&#xff0c;分布对⻬的场景时&#xff0c;我们可以优先考虑弹性盒布局。 任何一个容器都可以指定为Flex布局&#xff0c;容器设为 Flex 布局以后&#xff0…

电驱2035目标及新材料研究应用进展-2023

今天同大家一起了解DOE电驱2035目标&#xff08;成本、功率密度、电压、峰值功率&#xff09;&#xff0c;及当前研发项目中关于电驱电机的新材料研究进展与应用。 2030-2035电驱系统目标 峰值功率和功率密度按每5年50%的速度提升&#xff0c;电压平台800V&#xff0c;增加峰值…

数据被加密?.locked1勒索病毒的简单解决方法

引言&#xff1a; 在数字化的今天&#xff0c;数据宛如生命的一部分&#xff0c;而 .locked1 勒索病毒这种威胁正在如影随形地威胁着我们的数字宝库。本文将为您生动地介绍 .locked1 勒索病毒&#xff0c;以及如何摆脱它的束缚&#xff0c;解锁被其加密的数据&#xff0c;同时提…

Unity Golang项目教程-创建项目

安装Unity Unity的安装比较简单。这里我不做详细介绍&#xff0c;提供一些安装教程链接&#xff0c;如果还有困难下面我提供联系方式可以私信我。 安装教程参考 创建工程如下图所示&#xff1a; 等待项目创建完成即可。 如有问题可以Q联系我&#xff0c; 873149745

CCF中国开源大会专访 | 刘旭东:着眼“开源联合”,实现“聚力共赢”

受访嘉宾 | 刘旭东 记者 | 朱珂欣 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 2023 CCF 中国开源大会&#xff08; CCF ChinaOSC &#xff09;拟于 2023 年 10 月 21 日至 22 日在湖南省长沙市北辰国际会议中心召开。 作为第二届 CCF 中国开源大会&a…

计算CPK及规范限合格率,并绘制概率密度曲线

CPK&#xff08;过程能力指数&#xff09;是一个用于衡量一个过程的稳定性和一致性的统计指标&#xff0c;特别用于制造业和质量管理中。它衡量了一个过程的变异性与规范界限的关系&#xff0c;帮助确定过程是否能够产生合格的产品或服务。 正态分布假设&#xff1a;CPK的计算…

Kfka监控工具--Kafka-eagle安装

1、开启Kafka JMX端口 JMX 是一个为应用程序植入管理功能的框架 在启动Kafka脚本之前&#xff0c;添加&#xff1a; export JMX_PORT9988 nohup bin/kafka-server-start.sh comfig/server.properties 2、安装jdk配置好JAVA_HOME 3、将kafka_eagle 上传并解压 tar -zxvf …

想做扫码看图效果,你需要学会这一招

现在扫描二维码来查看图片是常见的一种方式&#xff0c;比如资料图片、个人照片、pdf图片、表情包等等都可以通过扫码的方式来展现。而且生成图片二维码的方法也非常的简单&#xff0c;只需要使用二维码在线生成器&#xff0c;就可以使用浏览器来快速完成制作&#xff0c;对于还…

VS2022+qt5.15.2+cmake3.23.2配置VTK9.1.0版本

VS2022qt5.15.2cmake3.23.2VTK9.1.0 尝试了好多次&#xff0c;终于成了~ 软件安装 先把需要的软件都安装好&#xff01; VS2022安装教程: https://blog.csdn.net/qq_44005305/article/details/132295064 qt5.15.2安装教程&#xff1a;https://blog.csdn.net/Qi_1337/article…

python—如何提取word中指定内容

假设有一个Word&#xff0c;该Word中存在 “联系人” 关键字&#xff0c;如何将该Word中的联系人所对应的内容提取出来呢&#xff1f; 该Word内容如下所示&#xff1a; 要在给定的Word文档中提取出与"联系人"关键字对应的内容&#xff0c;可以使用Python的py…

qt开发从入门到实战2

以下是本人学习笔记 原视频&#xff1a;最新QT从入门到实战完整版|传智教育 qt开发从入门到实战1 练习示例 设计一个按钮&#xff0c;点击时弹出新窗口&#xff0c;再次点击时新窗口关闭 // exerciseQWidget* second_window new QWidget();QPushButton* btn3 new QPushBu…

兽药经营小程序微信商城的作用是什么

无论家宠还是畜牧养殖&#xff0c;生病杀虫总是不可少的&#xff0c;尤其对铲屎官们来说&#xff0c;宠物的健康状况很重要&#xff0c;以此花费百元千元也并不觉心疼&#xff0c;兽药的需求度也是非常高&#xff0c;那么对相关从业商家来说&#xff0c;遇到的难题有哪些&#…