Java实现分片上传(前端分,后端合并)

注:一些实体类数据自行修改 

1,api文件 

import request from '@/router/axiosInfo';export const uploadChunk = (data) => {return request({url: '/api/blade-sample/sample/covid19/uploadChunk',method: 'post',data})
}export const uploadMerge = (data) => {return request({url: '/api/blade-sample/sample/covid19/uploadMerge',method: 'post',data})
}

2, axios文件

/*** 全站http配置** axios参数说明* isSerialize是否开启form表单提交* isToken是否需要token*/
import axios from 'axios';
import store from '@/store/';
import router from '@/router/router';
import { serialize } from '@/util/util';
import { getToken } from '@/util/auth';
import { Message } from 'element-ui';
import { isURL } from "@/util/validate";
import website from '@/config/website';
import { Base64 } from 'js-base64';
import { baseUrl } from '@/config/env';
// import NProgress from 'nprogress';
// import 'nprogress/nprogress.css';
import crypto from '@/util/crypto';//默认超时时间
axios.defaults.timeout = 100000;
//返回其他状态码
axios.defaults.validateStatus = function (status) {return status >= 200 && status <= 500;
};
//跨域请求,允许保存cookie
axios.defaults.withCredentials = true;
// NProgress 配置
// NProgress.configure({
//   showSpinner: false
// });
//http request拦截
axios.interceptors.request.use(config => {//开启 progress bar// NProgress.start();//地址为已经配置状态则不添加前缀if (!isURL(config.url) && !config.url.startsWith(baseUrl)) {config.url = baseUrl + config.url}//headers判断是否需要const authorization = config.authorization === false;if (!authorization) {config.headers['Authorization'] = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`;}//headers判断请求是否携带tokenconst meta = (config.meta || {});const isToken = meta.isToken === false;//headers传递token是否加密const cryptoToken = config.cryptoToken === true;//判断传递数据是否加密const cryptoData = config.cryptoData === true;const token = getToken();if (token && !isToken) {config.headers[website.tokenHeader] = cryptoToken? 'crypto ' + crypto.encryptAES(token, crypto.cryptoKey): 'bearer ' + token;}// 开启报文加密if (cryptoData) {if (config.params) {const data = crypto.encryptAES(JSON.stringify(config.params), crypto.aesKey);config.params = { data };}if (config.data) {config.text = true;config.data = crypto.encryptAES(JSON.stringify(config.data), crypto.aesKey);}}//headers中配置text请求if (config.text === true) {config.headers["Content-Type"] = "text/plain";}//headers中配置serialize为true开启序列化if (config.method === 'post' && meta.isSerialize === true) {config.data = serialize(config.data);}return config
}, error => {return Promise.reject(error)
});
//http response 拦截
axios.interceptors.response.use(res => {//关闭 progress bar// NProgress.done();//获取状态码const status = res.data.code || res.status;const statusWhiteList = website.statusWhiteList || [];const message = res.data.msg || res.data.error_description || '未知错误';const config = res.config;const cryptoData = config.cryptoData === true;//如果在白名单里则自行catch逻辑处理if (statusWhiteList.includes(status)) return Promise.reject(res);//如果是401则跳转到登录页面if (status === 401) store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }));// 如果请求为非200否者默认统一处理if (status !== 200) {Message({message: message,type: 'error'});return Promise.reject(new Error(message))}// 解析加密报文if (cryptoData) {res.data = JSON.parse(crypto.decryptAES(res.data, crypto.aesKey));}return res;
}, error => {// NProgress.done();return Promise.reject(new Error(error));
});export default axios;

3,vue源码 

<template><el-upload class="upload-demo" :on-change="handleChange" :show-file-list="false" :limit="1"><el-button slot="trigger" size="small" type="primary">选取文件</el-button><el-button style="margin-left: 10px;" size="small" type="success" @click="uploadMerge" v-loading.fullscreen.lock='loading'>上传到服务器</el-button><el-progress :percentage="percentage" :color="customColor"></el-progress></el-upload>
</template>
<script>import { uploadMerge, uploadChunk } from "@/api/sample/sampleCovid19Info";export default {data() {return {percentage: 0,customColor: '#409eff',fileList: [], // 文件列表  chunks: [], // 切片数组  currentChunkIndex: 0, // 当前切片索引  totalChunks: 0, // 总切片数  currentFile: null, // 当前处理文件  totalSize: 0,loading: false};},methods: {async uploadMerge() {this.loading = trueconst formData = new FormData();formData.append('fileName', this.currentFile.name);formData.append('totalSize', this.totalSize);console.log(22222);try {let res = await uploadMerge({ fileName: this.currentFile.name, totalSize: this.totalSize })console.log(res, '2');if (res.data.code === 200) {this.fileList = []this.chunks = []this.currentChunkIndex = 0this.totalChunks = 0this.currentFile = nullthis.totalSize = 0this.$message({type: "success",message: "添加成功!"});this.loading = false} else {this.$message({type: "success",message: "添加失败!"});this.loading = false}} catch (error) {this.$message.error(error)} finally {this.loading = false}},handleChange(file, fileList) {this.totalSize = file.size// 当文件变化时,执行切片操作  if (file.status === 'ready') {this.currentFile = file.raw; // 获取原始文件对象  this.sliceFile(this.currentFile);}},async sliceFile(file) {const chunkSize = 1024 * 1024 * 10; // 切片大小设置为1MB  const totalSize = file.size;this.totalChunks = Math.ceil(totalSize / chunkSize);for (let i = 0; i < this.totalChunks; i++) {const start = i * chunkSize;const end = Math.min(start + chunkSize, totalSize);const chunk = file.slice(start, end);this.chunks.push(chunk);const formData = new FormData();formData.append('chunk', chunk);formData.append('chunkIndex', i);formData.append('totalChunks', this.totalChunks);formData.append('fileName', this.currentFile.name);formData.append('totalSize', this.totalSize);let res = await uploadChunk(formData)if (res.data.code === 200) {this.currentChunkIndex = i + 1;if (this.percentage != 99) {this.percentage = i + 1;}//判断文件是否上传完成if (this.currentChunkIndex === this.totalChunks) {this.percentage = 100this.$message({type: "success",message: "上传成功!"});}}}}}
}
</script>

4,切片请求接口

/*** 大上传文件*/
@PostMapping("/uploadChunk")
@ApiOperationSupport(order = 15)
@ApiOperation(value = "大上传文件", notes = "大上传文件")
public R uploadChunk(@RequestParam("chunk") MultipartFile chunk,@RequestParam("chunkIndex") Integer chunkIndex,@RequestParam("totalChunks") Integer totalChunks,@RequestParam("fileName") String fileName,@RequestParam("totalSize") Long totalSize) throws IOException {Boolean isOk = sampleCovid19Service.LargeFiles(chunk, chunkIndex, totalChunks, fileName, totalSize);return R.status(isOk);
}
/*** 大上传文件* @param chunk 文件流* @param chunkIndex 切片索引* @param totalChunks 切片总数* @param fileName 文件名称* @param totalSize 文件大小* @return*/
Boolean LargeFiles(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String fileName, Long totalSize);

 

/*** 大上传文件* @param chunk 文件流* @param chunkIndex 切片索引* @param totalChunks 切片总数* @param fileName 文件名称* @param totalSize 文件大小* @return*/
@Override
public Boolean LargeFiles(MultipartFile chunk, Integer chunkIndex, Integer totalChunks, String fileName, Long totalSize) {if (chunk == null) {throw new ServiceException("添加失败,文件名称获取失败");}if (chunkIndex == null) {throw new ServiceException("添加失败,分片序号不能为空");}if (fileName == null) {throw new ServiceException("添加失败,文件名称获取失败");}if (totalSize == null || totalSize == 0) {throw new ServiceException("添加失败,文件名大小不能为空或0");}//获取文件名称fileName = fileName.substring(0, fileName.lastIndexOf("."));File chunkFolder = new File(chunkPath + totalSize + "/" + fileName);//判断文件路径是否创建if (!chunkFolder.exists()) {chunkFolder.mkdirs();}//直接将文件写入File file = new File(chunkPath + totalSize + "/" + fileName + "/" + chunkIndex);try {chunk.transferTo(file);} catch (IOException e) {log.info("时间:{}" + new Date() + "文件名称:{}" + fileName + "上传失败");throw new ServiceException("时间:{}" + new Date() + "文件名称:{}" + fileName + "上传失败");}return true;
}

 5,合并接口

package org.springblade.sample.vo;import lombok.Data;@Data
public class UploadMergeVO {//文件上传名称private String fileName;//文件大小private Long totalSize;
}
/*** 大文件合并*/
@PostMapping("/uploadMerge")
@ApiOperationSupport(order = 16)
@ApiOperation(value = "文件合并", notes = "文件合并")
public R uploadMerge(@RequestBody UploadMergeVO vo) throws IOException {Boolean isOk = sampleCovid19Service.LargeMerge(vo);return R.status(isOk);
}
/***  大文件合并* @param vo * @return* @throws IOException*/
Boolean LargeMerge(UploadMergeVO vo) throws IOException;

 

/***  大文件合并* @param vo* @return* @throws IOException*/
@Override
public Boolean LargeMerge(UploadMergeVO vo) throws IOException {if (StringUtil.isBlank(vo.getFileName())) {throw new ServiceException("上传失败,文件名称获取失败");}if (vo.getTotalSize() == 0) {throw new ServiceException("上传失败,文件大小不能为空");}//块文件目录String fileNameInfo = vo.getFileName().substring(0, vo.getFileName().lastIndexOf("."));//合并文件夹File chunkFolder = new File(chunkPath + vo.getTotalSize() + "/" + fileNameInfo + "/");File depositFile = new File(sourceFile + vo.getTotalSize() + "/" + fileNameInfo + "/");//创建文件夹if (!depositFile.exists()) {depositFile.mkdirs();}//创建新的合并文件File largeFile = new File(depositFile + "/" + vo.getFileName());largeFile.createNewFile();//用于写文件RandomAccessFile raf_write = new RandomAccessFile(largeFile, "rw");//指针指向文件顶端raf_write.seek(0);//缓冲区byte[] b = new byte[1024];//分块列表File[] fileArray = chunkFolder.listFiles();// 转成集合,便于排序List<File> fileList = Arrays.asList(fileArray);// 从小到大排序Collections.sort(fileList, new Comparator<File>() {@Overridepublic int compare(File o1, File o2) {return Integer.parseInt(o1.getName()) - Integer.parseInt(o2.getName());}});//合并文件for (File chunkFile : fileList) {RandomAccessFile raf_read = new RandomAccessFile(chunkFile, "rw");int len = -1;while ((len = raf_read.read(b)) != -1) {raf_write.write(b, 0, len);}raf_read.close();}raf_write.close();//取出合并文件大小和hashlong fileSize = getFileSize(largeFile);int hashCode = largeFile.hashCode();//判断是否合并成功if (fileSize == vo.getTotalSize()) {//将内容写入数据库FileBack fileBack = new FileBack();fileBack.setFileName(vo.getFileName());fileBack.setFileSize(Integer.parseInt(String.valueOf(vo.getTotalSize())));fileBack.setUpdateTime(new Date());fileBack.setFileUrl(chunkFolder.toString());fileBack.setFileHash(String.valueOf(hashCode));fileBack.setUploadData(new Date());fileBackService.save(fileBack);//删除临时文件目录File chunkFolderInfo = new File(chunkPath + vo.getTotalSize() + "/");FileUtils.deleteDirectory(chunkFolderInfo);log.info("时间:{}" + new Date() + "文件名称:{}" + vo.getFileName() + "上传成功");return true;} else {//删除临时文件目录File chunkFolderInfo = new File(chunkPath + vo.getTotalSize() + "/");FileUtils.deleteDirectory(chunkFolderInfo);log.info("时间:{}" + new Date() + "文件名称:{}" + vo.getFileName() + "上传失败");return false;}
}

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

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

相关文章

AI预测体彩排列3第2套算法实战化测试第1弹2024年4月22日第1次测试

从今天开始&#xff0c;开始新一轮的测试&#xff0c;本轮测试&#xff0c;以6码为基础&#xff0c;同步测试杀号情况&#xff0c;争取杀至4-5码。经过计算&#xff0c;假如5码命中&#xff0c;即每期125注&#xff0c;投入250元&#xff0c;十期共计2500元&#xff0c;则命中率…

牛客NC233 加起来和为目标值的组合(四)【中等 DFS C++、Java、Go、PHP】

题目 题目链接&#xff1a; https://www.nowcoder.com/practice/7a64b6a6cf2e4e88a0a73af0a967a82b 解法 dfs参考答案C class Solution {public:/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可*** param nums int整型…

日本二次元团建国内院线:一周一部,占据36.2%票房

从《你想活出怎样的人生》开始&#xff0c;到《哈尔的移动城堡》结束&#xff0c;日本动画正在占据国内院线的整个4月份档期。 包括《数码宝贝02&#xff1a;最初的召唤》、《间谍过家家 代号&#xff1a;白》多部作品在内&#xff0c;整个国内四月份院线日本动画平均一周上映…

【Linux实践室】Linux高级用户管理实战指南:Linux用户与用户组编辑操作详解

&#x1f308;个人主页&#xff1a;聆风吟_ &#x1f525;系列专栏&#xff1a;Linux实践室、网络奇遇记 &#x1f516;少年有梦不应止于心动&#xff0c;更要付诸行动。 文章目录 一. ⛳️任务描述二. ⛳️相关知识2.1 &#x1f514;Linux查看用户属性命令2.1.1 &#x1f47b;…

快手视频下载下来水印怎么去掉

在快手应用中浏览或上传视频时&#xff0c;可能会遇到带有水印的影片&#xff0c;这些水印可能会影响到视频的观看体验。虽然快手也提供了去除水印的功能&#xff0c;但这个功能并不是免费的。因此&#xff0c;本文将介绍一种简单的方法来保存无水印版本的快手视频。 快手视频…

数据结构PT1——线性表/链表

1&#xff1a;顺序存储实现(数组实现) Data&#xff1a; a1 a2 .....ai ai1 .... an .... typedef struct LNode *List; //指向LNode的指针&#xff0c;这是typedef的&#xff0c;你可以随时声明&#xff0c;而不加typedef只是创建一个 struct LNode{ //结构体成员ElementT…

查询效率至少提高4倍的MySQL技巧

SQL语句中IN包含的值不应过多 MySQL对于IN做了相应的优化&#xff0c;即将IN中的常量全部存储在一个数组里面&#xff0c;而且这个数组是排好序的。但是如果数值较多&#xff0c;产生的消耗也是比较大的。再例如&#xff1a;select id from t where num in(1,2,3) 对于连续的数…

【免费题库】华为OD机试 - 根据IP查找城市(Java JS Python C C++)

须知 哈喽,本题库完全免费,收费是为了防止被爬,大家订阅专栏后可以私信联系退款。感谢支持 文章目录 须知题目描述输入描述输出描述解题思路:Java代码:JS代码:Python代码:C++代码:题目描述 某业务需要根据终端的IP地址获取该终端归属的城市,可以根据公开的IP地址池信…

Uipath用计划任务启动 bat脚本语句

方法一 cd C:\Program Files\UiPath\Studio UiRobot execute --file "Uipath路径\project.json" 这种是用UiRobot.exe来启动任务的 方法二 "C:\Progra~1\UiPath\Studio\UiRobot.exe" -file "C:\Users\Documents\UiPath\se_Windows.1.0.3.nupkg&q…

【前端】掌握按钮的显示与隐藏

目录 一、前言二、实现显示与隐藏的四种方式1、CSS属性控制2、JavaScript控制3、Vue.js响应式控制 三、需求背景四、代码实现1、定义变量2、绑定事件3、监听选中的学生4、返回return 五、整体效果1、没有勾选学生2、已勾选学生 六、设置按钮显示与隐藏的优势利弊1、优势2、劣势…

计算机网络面试问题

文章目录 1.DHCP协议:动态分配IP地址2.SNMP协议3.HTTP协议4.DNS协议1.DHCP协议:动态分配IP地址 (1)作用: DHCP服务器动态地将IP池里的IP地址分配给新加入网络的主机。基于4次UDP广播。 (2)DHCP的四种报文: 报文名源IP地址目的IP地址①DHCP发现报文 Discover0.0.0.0255.25…

MySQL慢查询怎么办?需要关注Explain的哪些关键字?

目录 1-引言&#xff1a;什么是慢查询1-1 慢查询定义1-2 为什么排查慢查询 2-核心&#xff1a;慢查询排查2-1 慢查询定位2-2 慢查询解决2-2-1 Explain 排查慢查询2-2-2 Explain 重点关键字 3-总结&#xff1a;慢查询知识点小结 1-引言&#xff1a;什么是慢查询 1-1 慢查询定义…

C# 字面量null对于引用类型变量✓和值类型变量×

编译器让相同的字符串字面量共享堆中的同一内存位置以节约内存。 在C#中&#xff0c;字面量&#xff08;literal&#xff09;是指直接表示固定值的符号&#xff0c;比如数字、字符串或者布尔值。而关键字&#xff08;keyword&#xff09;则是由编程语言定义的具有特殊含义的标…

微信小程序实现腾讯地图

1、// map.js Page({ data: { latitude: 0, // 初始纬度值 longitude: 0, // 初始经度值 markers: [] }, onReady: function (e) { this.getLocation(); // 主动获取用户地理位置信息 }, getLocation: function () { wx.getLocation({ type: gcj02, success: (res) > { thi…

羊大师解读,春季羊奶VS夏季羊奶

羊大师解读&#xff0c;春季羊奶VS夏季羊奶 夏季的羊奶和春季的羊奶在营养成分上并没有本质的区别&#xff0c;都含有丰富的蛋白质、矿物质和维生素等。然而&#xff0c;由于季节和气候的变化&#xff0c;人们饮用羊奶的需求和效果可能会有所不同。 在夏季&#xff0c;天气炎热…

C语言 | Leetcode C语言题解之第35题搜索插入位置

题目&#xff1a; 题解&#xff1a; int searchInsert(int* nums, int numsSize, int target) {int left 0, right numsSize - 1, ans numsSize;while (left < right) {int mid ((right - left) >> 1) left;if (target < nums[mid]) {ans mid;right mid - …

R语言 数据的整理与清洗(第一篇)

《Cookbook for R》 Manipulating Data ~ General Sorting 排序 1、vectors 向量 # 先随机生成一组向量 v <- sample(101:110)# 排序sort() sort(v) #> [1] 101 102 103 104 105 106 107 108 109 110# 反向排序 sort(v, decreasingTRUE) #> [1] 110 109 108 107 1…

Docker容器的使用与操作

1、什么是容器 镜像和容器的关系&#xff0c;就像是面向对象程序设计中的类和实例一样&#xff0c;镜像是静态的定义&#xff0c;容器是镜像运行时的实体&#xff0c;基于同一镜像可以创建若干个不同的容器。 每个容器都有一个软件镜像。可以将容器看作一个将应用程序及其依赖环…

使用vue3+ts+vite从零开始搭建bolg(二)

二、全局变量 2.1element-ui集成 pnpm i element-plus pnpm i element-plus element-plus/icons-vue main.ts配置文件 import ElementPlus from element-plus import element-plus/dist/index.css //ts-ignore import zhCn from element-plus/dist/locale/zh-cn.mjsapp.use…