java大文件分片上传

1.效果图

2.前端html

<!DOCTYPE html>
<html>
<head></head>
<body>
<form><input type="file" id="fileInput" multiple><button type="button" onclick="upload()" >大文件分片上传</button>
</form>
<script>function upload() {var fileInput = document.getElementById('fileInput');var fileName = document.getElementById("fileInput").files[0].name;var files = fileInput.files;var chunkSize = 1024 * 1024 * 10; // 每个块的大小为10MBvar totalChunks = Math.ceil(files[0].size / chunkSize); // 文件总块数var currentChunk = 0; // 当前块数console.log("当前文件:"+fileName+",大小:"+files[0].size+",分片大小:"+chunkSize+",分片数:"+totalChunks);// 分片上传文件function uploadChunk() {var xhr = new XMLHttpRequest();var formData = new FormData();// 将当前块数和总块数添加到formData中formData.append('current_slice_index', currentChunk);formData.append('file_name', fileName);formData.append('user_name', "15910761260");// 计算当前块在文件中的偏移量和长度var start = currentChunk * chunkSize;var end = Math.min(files[0].size, start + chunkSize);var chunk = files[0].slice(start, end);// 添加当前块到formData中formData.append('file', chunk);// 发送分片到后端xhr.open('POST', 'http://192.x.x.x:8060/file/fileInfo/uploadSlice');xhr.send(formData);xhr.onload = function(data) {console.log('上传第'+ currentChunk +'个分片结果:'+xhr.responseText);// 需要判断反馈结果,如果成功,才继续,否则再次上传失败部分// 更新当前块数currentChunk++;// 如果还有未上传的块,则继续上传if (currentChunk < totalChunks) {uploadChunk();} else {// 所有块都上传完毕,进行文件合并console.log("开始合并");mergeChunks(fileName);}}}// 合并所有分片function mergeChunks() {var xhr = new XMLHttpRequest();var formData = new FormData();formData.append('user_name', "15910761260");formData.append('total_slice_num', totalChunks);formData.append('file_name', fileName);formData.append('file_owner', 'MediaResource');formData.append('order_code', 'MediaResource');formData.append('folder', 'MediaResource');xhr.open("POST", "http://192.x.x.x:8060/file/fileInfo/mergeSlice");xhr.onreadystatechange = function() {if (xhr.readyState === 4) {if (xhr.status === 200) {console.log("文件合并完成:", xhr.responseText);} else {console.error(xhr.responseText);}}};xhr.send(formData);}// 开始上传uploadChunk();}
</script>
</body>
</html>

2.java代码

        上传分片代码

    @ResponseBody@PostMapping(value = "/uploadSlice")@Operation(summary = "上传文件分片", description = "返回是否上传成功")public MisResult<String> UploadSlice(UploadFileSliceInput uploadFileSliceInput,@RequestParam(value = "file") MultipartFile multipartFile) throws Exception {return fileInfoService.UploadSlice(uploadFileSliceInput, multipartFile.getBytes());}@Overridepublic MisResult<String> UploadSlice(UploadFileSliceInput uploadFileSliceInput, byte[] content) {MisResult<String> result = new MisResult<>();try {//参数校验ValidateUtil.ValidateThrowException(uploadFileSliceInput);if (content == null || content.length == 0) {result.Error(MisResultCode.PARAM_ERROR);return result;}//判断分片是否已上传String nameMd5 = DigestUtils.md5Hex(uploadFileSliceInput.getFile_name());String redisKey = nameMd5 + "_" + uploadFileSliceInput.getUser_name();String chunkStatus = (String) RedisUtil.HashGet(redisKey, uploadFileSliceInput.getCurrent_slice_index() + "");if (StringUtils.hasLength(chunkStatus) && MisStatus.Finished.equals(chunkStatus)) {result.Success("文件分片上传成功!");return result;}//保存文件分片到临时文件夹String absolutePath = FileConfig.Path + File.separator + "temp" + File.separator + "slice_" + nameMd5+ "_" + uploadFileSliceInput.getCurrent_slice_index();File saveFile = new File(absolutePath);FileCopyUtils.copy(content, saveFile);//记录上传状态long timeout = RedisUtil.GetExpire(redisKey);RedisUtil.HashSet(redisKey, uploadFileSliceInput.getCurrent_slice_index() + "", MisStatus.Finished);result.Success("文件分片上传成功!");if (timeout < 0) {RedisUtil.SetExpire(redisKey, 60 * 60);//1个小时超时删除}} catch (Exception e) {result.Error(MisResultCode.UNKNOWN_ERROR, "分片上传异常," + e.getMessage());log.error("分片上传异常", e);}return result;}

        上传参数

package mis.dto.file.fileinfo;import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;/*** description 分片上传文件dto*/
@Schema(description = "分片上传文件dto")
@Data
@Accessors(chain = true)
public class UploadFileSliceInput {@NotNull@Schema(description = "上传手机号")private String user_name;@NotNull@Schema(description = "文件名")private String file_name;@NotNull@Schema(description = "当前分片序号")private int current_slice_index;}

          合并分片代码

    @ResponseBody@PostMapping(value = "/mergeSlice")@Operation(summary = "合并文件分片", description = "返回是否上传成功")public MisResult<FileInfoOutput> MergeSlice(UploadFileSliceMerge uploadFileSliceMerge) throws Exception {return fileInfoService.MergeSlice(uploadFileSliceMerge);}@Overridepublic MisResult<FileInfoOutput> MergeSlice(UploadFileSliceMerge uploadFileSliceMerge) throws Exception {MisResult<FileInfoOutput> result = new MisResult<>();//参数校验,非空项及上传分片数量是否正确ValidateUtil.ValidateThrowException(uploadFileSliceMerge);String nameMd5 = DigestUtils.md5Hex(uploadFileSliceMerge.getFile_name());String redisKey = nameMd5 + "_" + uploadFileSliceMerge.getUser_name();Map<Object, Object> sliceMap = RedisUtil.HashGetMap(redisKey);if (sliceMap.size() != uploadFileSliceMerge.getTotal_slice_num()) {result.Error(MisResultCode.MIS_ERR_NOTEXITS, "文件分片上传不完整," + uploadFileSliceMerge.getUser_name());log.error(result.getMessage());return result;}//根据分片文件序号排列File tmpDir = new File(FileConfig.Path + File.separator + "temp");File[] sliceFiles = tmpDir.listFiles((dir, name) -> name.contains("slice") &&name.contains(nameMd5));if (sliceFiles == null) {result.Error(MisResultCode.MIS_ERR_NOTEXITS, "文件分片不存在!");return result;}Arrays.sort(sliceFiles, (o1, o2) -> {String o1Index = o1.getName().replace("_", "").replace("slice", "").replace(nameMd5, "");String o2Index = o2.getName().replace("_", "").replace("slice", "").replace(nameMd5, "");return Integer.parseInt(o1Index) - Integer.parseInt(o2Index);});//设置最终存储路径String fileOwner = StringUtils.hasLength(uploadFileSliceMerge.getFile_owner()) ?uploadFileSliceMerge.getFile_owner() : "unknown";//文件拥有者String newFileName = UuidGenerator.generate() + "." + FileUtil.GetSuffix(uploadFileSliceMerge.getFile_name());//新文件名String storageDir = FileConfig.Path + File.separator;if (StringUtils.hasLength(uploadFileSliceMerge.getFolder())) {storageDir += uploadFileSliceMerge.getFolder();//存储目录} else {storageDir += DateUnitl.ToString(null, "yyyyMM") + File.separator + fileOwner;//存储目录}if (!new File(storageDir).exists()) {new File(storageDir).mkdirs();}String absolutePath = storageDir + File.separator + newFileName;//存储绝对路径String filePath = absolutePath.replace(FileConfig.Path, "");//存储相对路径//合并分片文件FileChannel outChannel = null;FileOutputStream outputStream = null;try {outputStream = new FileOutputStream(absolutePath);outChannel = outputStream.getChannel();for (int i = 0; i < sliceFiles.length; i++) {FileInputStream inputStream = new FileInputStream(sliceFiles[i]);try (FileChannel inChannel = inputStream.getChannel()) {inChannel.transferTo(0, inChannel.size(), outChannel);FileUtil.Close(inChannel);}FileUtil.Close(inputStream);}} catch (Exception e) {log.error("合并分片异常", e);result.Error(MisResultCode.ACTION_ERROR, "合并分片异常," + uploadFileSliceMerge.getFile_name());return result;} finally {FileUtil.Close(outChannel);FileUtil.Close(outputStream);}//保存最终文件信息File bigFile = new File(absolutePath);String uuid = UuidGenerator.generate();String downloadUrl = FileConfig.Access + "?fileInfoId=" + uuid;FileInfo newFileInfo = new FileInfo(uuid).setFile_owner(fileOwner).setOrder_code(uploadFileSliceMerge.getOrder_code()).setFile_name(uploadFileSliceMerge.getFile_name()).setFile_path(filePath).setFile_absolute_path(absolutePath).setFile_size(bigFile.length()).setFile_type(FileUtil.JudgeFileTypeByName(uploadFileSliceMerge.getFile_name())).setFile_status(MisStatus.Enable).setDownload_path(downloadUrl).setNote(MisServerConfig.App);var savedFile = this.fileInfoRepository.save(newFileInfo);//清理分片和缓存for (File sliceFile : sliceFiles) {sliceFile.delete();}RedisUtil.Delete(nameMd5 + "_" + uploadFileSliceMerge.getUser_name());//返回文件对象var modelMapper = new ModelMapper();var fileInfoOutput = modelMapper.map(savedFile, FileInfoOutput.class);fileInfoOutput.setPreview_path("/file/fileInfo/preview?fileInfoId=" + uuid);result.Success(fileInfoOutput);return result;}

        合并分片参数

package mis.dto.file.fileinfo;import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.experimental.Accessors;/*** description 合并分片参数dto*/
@Schema(description = "分片上传文件dto")
@Data
@Accessors(chain = true)
public class UploadFileSliceMerge {//合并参数@NotNull@Schema(description = "上传手机号")private String user_name;@NotNull@Schema(description = "总分片数")private double total_slice_num;//文件参数@NotNull@Schema(description = "文件名")private String file_name;//文件名@Schema(description = "文件持有人")private String file_owner;//文件持有人@Schema(description = "单据编码")private String order_code;//单据编码@Schema(description = "文件夹,如果文件夹不为空,则使用传入的文件夹保存文件")private String folder;//文件夹,如果文件夹不为空,则使用传入的文件夹保存文件}

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

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

相关文章

计算机网络_1.3电路交换、分组交换和报文交换

1.3电路交换、分组交换和报文交换 一、电路交换1、“电路交换”例子引入2、电路交换的三个阶段3、计算机之间的数据传送不适合采用电路交换 二、分组交换1、发送方&#xff08;1&#xff09;报文&#xff08;2&#xff09;分组&#xff08;3&#xff09;首部 2、交换节点3、接收…

LeetCode 使循环数组所有元素相等的最少秒数

地址&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 难度&#xff1a;中等 题目描述&#xff1a;给你一个下标从 0 开始长度为 n 的数组 nums 。 每一秒&#xff0c;你可以对数组执行以下操作&#xff1a; 对于范围在 [0, n - 1] 内的每…

代码随想录算法训练营第二十二天 |235. 二叉搜索树的最近公共祖先,701.二叉搜索树中的插入操作,450.删除二叉搜索树中的节点(待补充)

235.二叉搜索树的最近公共祖先 1、题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 2、文章讲解&#xff1a;代码随想录 3、题目&#xff1a; 给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公…

深入理解二叉树:遍历、构建与性质探索的代码实现

&#x1f4f7; 江池俊&#xff1a; 个人主页 &#x1f525;个人专栏&#xff1a; ✅数据结构冒险记 ✅C语言进阶之路 &#x1f305; 有航道的人&#xff0c;再渺小也不会迷途。 文章目录 前言一、二叉树的存储结构二、二叉树链式结构的实现三、二叉树的前、中、后续遍历&…

【数据结构 07】AVL树

目录 一、二叉搜索树 二、AVL树 2.1 左单旋 2.2 右单旋 2.3 左右双旋 2.4 右左双旋 三、AVL.h 四、test.cpp 一、二叉搜索树 二叉搜索树&#xff0c;又称二叉排序树&#xff08;Binary Search Tree&#xff09;&#xff0c;相比于普通二叉树&#xff0c;BST的特性有&a…

UE5 C++ 读取本地图片并赋值到UI上

目录 结果图 节点样式 主要代码 调试代码 结果图 节点样式 主要代码 &#xff08;注释纯属个人理解&#xff0c;可能存在错误&#xff09; // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h&q…

Java面向对象详解

面向对象和面向过程的区别&#xff1a; 面向对象和面向过程都是对软件分析、设计和开发的一种思想&#xff0c;它指导着人们以不同的方式去分析、设计和开发软件。C语言是一种典型的面向过程语言&#xff0c;Java是一种典型的面向对象语言。 面向过程适合简单、不需要协作的事务…

Optimism的挑战期

1. 引言 前序博客&#xff1a; Optimism的Fault proof 用户将资产从OP主网转移到以太坊主网时需要等待一周的时间。这段时间称为挑战期&#xff0c;有助于保护 OP 主网上存储的资产。 而OP测试网的挑战期仅为60秒&#xff0c;以简化开发过程。 2. OP与L1数据交互 L1&#xf…

探索智能巡检机器人深度学习的奥秘

机器人深度学习&#xff08;Robot Deep Learning&#xff09;是指利用深度学习技术&#xff0c;使机器人能够从大量数据中学习和提取特征&#xff0c;进而实现自主感知、决策和行动的能力。通过深度学习算法&#xff0c;机器人可以从传感器获取的数据中自动学习模式和规律&…

微信开放平台第三方开发,实现代小程序认证申请

大家好&#xff0c;我是小悟 微信小程序认证整体流程总共分为五个环节&#xff1a;认证信息填写、平台初审、管理员验证、供应商审核和认证成功。 服务商可以代小程序发起认证申请。平台将对认证基础信息进行初步校验。通过后&#xff0c;平台将向管理员微信下发模板消息&…

Redis(十)SpringBoot集成Redis

文章目录 连接单机mvnYMLController.javaRedisConfig.java 连接集群YML问题复现 RedisTemplate方式 连接单机 mvn <!--Redis--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</art…

SRC实战 | 信息泄露挖掘

本文由掌控安全学院 - 叴龙 投稿 1. 信息搜集 首先老语法先搜集一波&#xff0c;毕竟没有钓鱼和sg的能力&#xff0c;只能找注册站去挖挖了。 web.title”XX大学”&&web.body”忘记密码”&&web.body”注册” 2. 漏洞挖掘 这里找到一个可以注册网站接口&…

蓝桥杯 第 1 场 小白入门赛

目录 1.蘑菇炸弹 2.构造数字 3.小蓝的金牌梦 4.合并石子加强版 5.简单的LIS问题 6.期望次数 1.蘑菇炸弹 我们直接依照题目 在中间位置的数进行模拟即可 void solve(){cin>>n;vector<int> a(n1);for(int i1;i<n;i) cin>>a[i];int ans0;for(int i2;i…

XSS haozi靶场通关笔记

XSS靶场地址&#xff1a;alert(1) 靶场的要求是输出一个内容为1的弹窗&#xff1b;这个靶场限制了输入位置只能是input code&#xff1b;而且浏览器发送内容时会自动进行url编码&#xff1b;所以重点考察的是代码的分析和基础payload构造&#xff1b;一切完成在当前页面&#…

【数据结构】链表(单链表实现+测试+原码)

1.链表 1.1 链表的概念及结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素的逻辑顺序是通过链表 中的指针链接次序实现的 。 现实中&#xff1a;链表就像是一列动车&#xff0c;一节连着一节 数据结构中的链表 注意: 1.从上图可看出…

React16源码: React中LegacyContext的源码实现

LegacyContext 老的 contextAPI 也就是我们使用 childContextTypes 这种声明方式来从父节点为它的子树提供 context 内容的这么一种方式遗留的contextAPI 在 react 17 被彻底移除了&#xff0c;就无法使用了那么为什么要彻底移除这个contextAPI的使用方式呢&#xff1f;因为它…

知识点积累系列(一)golang语言篇【持续更新】

云原生学习路线导航页&#xff08;持续更新中&#xff09; 本文是 知识点积累 系列文章的第一篇&#xff0c;记录golang语言相关的知识点 1.结构体的mapstructure是什么 mapstructure:"default" mapstructure是一个Go语言的库&#xff0c;用于将一个map中的值映射到…

C语言王道第八周一题

Description 初始化顺序表&#xff08;顺序表中元素为整型&#xff09;&#xff0c;里边的元素是 1,2,3&#xff0c;然后通过 scanf 读取一个元素&#xff08;假如插入的是 6&#xff09;&#xff0c;插入到第 2 个位置&#xff0c;打印输出顺序表&#xff0c;每个 元素占 3 个…

添加了gateway之后远程调用失败

前端提示500&#xff0c;后端提示[400 ] during [GET] to [http://userservice/user/1] 原因是这个&#xff0c;因为在请求地址写了两个参数&#xff0c;实际上只传了一个参数 解决方案&#xff1a;加上(required false)并重启所有相关服务

【程序员英语】【美语从头学】初级篇(入门)(笔记)Lesson13(买东西)(餐厅点餐事宜;询问有无座位;食物如何调理:牛排、咖啡等;菜单等相关)

《美语从头学初级入门篇》 注意&#xff1a;被 删除线 划掉的不一定不正确&#xff0c;只是不是标准答案。 文章目录 Lesson 13 At the Restaurant 在餐厅会话A会话B笔记餐厅询问有无座位&#xff1b;餐厅电话订座其他餐厅询问有无座位的问法 吸烟区与非吸烟区&#xff08;smo…