package com.ruoyi.web.upload.service.impl;
import com.ruoyi.web.upload.dto.FileChunkDTO;
import com.ruoyi.web.upload.dto.FileChunkResultDTO;
import com.ruoyi.web.upload.service.IUploadService;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.*;
import java.util.*;
/**
* @ProjectName UploadServiceImpl
* @author Administrator
* @version 1.0.0
* @Description 附件分片上传
* @createTime 2022/4/13 0013 15:59
*/
@Service
@SuppressWarnings("all")
public class UploadServiceImpl implements IUploadService {
private Logger logger = LoggerFactory.getLogger(UploadServiceImpl.class);
@Autowired
private RedisTemplate redisTemplate;
@Value("${ruoyi.profile}")
private String uploadFolder;
/**
* 检查文件是否存在,如果存在则跳过该文件的上传,如果不存在,返回需要上传的分片集合
* 检查分片是否存在
○ 检查目录下的文件是否存在。
○ 检查redis存储的分片是否存在。
○ 判断分片数量和总分片数量是否一致。
如果文件存在并且分片上传完毕,标识已经完成附件的上传,可以进行秒传操作。
如果文件不存在或者分片为上传完毕,则返回false并返回已经上传的分片信息。
* @param chunkDTO
* @return
*/
@Override
public FileChunkResultDTO checkChunkExist(FileChunkDTO chunkDTO) {
//1.检查文件是否已上传过
//1.1)检查在磁盘中是否存在
String fileFolderPath = getFileFolderPath(chunkDTO.getIdentifier());
logger.info("fileFolderPath-->{}", fileFolderPath);
String filePath = getFilePath(chunkDTO.getIdentifier(), chunkDTO.getFilename());
File file = new File(filePath);
boolean exists = file.exists();
//1.2)检查Redis中是否存在,并且所有分片已经上传完成。
Set<Integer> uploaded = (Set<Integer>) redisTemplate.opsForHash().get(chunkDTO.getIdentifier(), "uploaded");
if (uploaded != null && uploaded.size() == chunkDTO.getTotalChunks() && exists) {
return new FileChunkResultDTO(true);
}
File fileFolder = new File(fileFolderPath);
if (!fileFolder.exists()) {
boolean mkdirs = fileFolder.mkdirs();
logger.info("准备工作,创建文件夹,fileFolderPath:{},mkdirs:{}", fileFolderPath, mkdirs);
}
// 断点续传,返回已上传的分片
return new FileChunkResultDTO(false, uploaded);
}
/**
* 上传分片
* 上传附件分片
○ 判断目录是否存在,如果不存在则创建目录。
○ 进行切片的拷贝,将切片拷贝到指定的目录。
○ 将该分片写入redis
* @param chunkDTO
*/
@Override
public void uploadChunk(FileChunkDTO chunkDTO) {
//分块的目录
String chunkFileFolderPath = getChunkFileFolderPath(chunkDTO.getIdentifier());
logger.info("分块的目录 -> {}", chunkFileFolderPath);
File chunkFileFolder = new File(chunkFileFolderPath);
if (!chunkFileFolder.exists()) {
boolean mkdirs = chunkFileFolder.mkdirs();
logger.info("创建分片文件夹:{}", mkdirs);
}
//写入分片
try (
InputStream inputStream = chunkDTO.getFile().getInputStream();
FileOutputStream outputStream = new FileOutputStream(new File(chunkFileFolderPath + chunkDTO.getChunkNumber()))
) {
IOUtils.copy(inputStream, outputStream);
logger.info("文件标识:{},chunkNumber:{}", chunkDTO.getIdentifier(), chunkDTO.getChunkNumber());
//将该分片写入redis
long size = saveToRedis(chunkDTO);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public boolean mergeChunk(String identifier, String fileName, Integer totalChunks) throws IOException {
return mergeChunks(identifier, fileName, totalChunks);
}
/**
* 合并分片
*
* @param identifier
* @param filename
*/
private boolean mergeChunks(String identifier, String filename, Integer totalChunks) {
String chunkFileFolderPath = getChunkFileFolderPath(identifier);
String filePath = getFilePath(identifier, filename);
// 检查分片是否都存在
if (checkChunks(chunkFileFolderPath, totalChunks)) {
File chunkFileFolder = new File(chunkFileFolderPath);
File mergeFile = new File(filePath);
File[] chunks = chunkFileFolder.listFiles();
// 切片排序1、2/3、---
List fileList = Arrays.asList(chunks);
Collections.sort(fileList, (Comparator<File>) (o1, o2) -> {
return Integer.parseInt(o1.getName()) - (Integer.parseInt(o2.getName()));
});
try {
RandomAccessFile randomAccessFileWriter = new RandomAccessFile(mergeFile, "rw");
byte[] bytes = new byte[1024];
for (File chunk : chunks) {
RandomAccessFile randomAccessFileReader = new RandomAccessFile(chunk, "r");
int len;
while ((len = randomAccessFileReader.read(bytes)) != -1) {
randomAccessFileWriter.write(bytes, 0, len);
}
randomAccessFileReader.close();
}
randomAccessFileWriter.close();
} catch (Exception e) {
return false;
}
return true;
}
return false;
}
/**
* 检查分片是否都存在
* @param chunkFileFolderPath
* @param totalChunks
* @return
*/
private boolean checkChunks(String chunkFileFolderPath, Integer totalChunks) {
try {
for (int i = 1; i <= totalChunks + 1; i++) {
File file = new File(chunkFileFolderPath + File.separator + i);
if (file.exists()) {
continue;
} else {
return false;
}
}
} catch (Exception e) {
return false;
}
return true;
}
/**
* 分片写入Redis
* 判断切片是否已存在,如果未存在,则创建基础信息,并保存。
* @param chunkDTO
*/
private synchronized long saveToRedis(FileChunkDTO chunkDTO) {
Set<Integer> uploaded = (Set<Integer>) redisTemplate.opsForHash().get(chunkDTO.getIdentifier(), "uploaded");
if (uploaded == null) {
uploaded = new HashSet<>(Arrays.asList(chunkDTO.getChunkNumber()));
HashMap<String, Object> objectObjectHashMap = new HashMap<>();
objectObjectHashMap.put("uploaded", uploaded);
objectObjectHashMap.put("totalChunks", chunkDTO.getTotalChunks());
objectObjectHashMap.put("totalSize", chunkDTO.getTotalSize());
// objectObjectHashMap.put("path", getFileRelativelyPath(chunkDTO.getIdentifier(), chunkDTO.getFilename()));
objectObjectHashMap.put("path", chunkDTO.getFilename());
redisTemplate.opsForHash().putAll(chunkDTO.getIdentifier(), objectObjectHashMap);
} else {
uploaded.add(chunkDTO.getChunkNumber());
redisTemplate.opsForHash().put(chunkDTO.getIdentifier(), "uploaded", uploaded);
}
return uploaded.size();
}
/**
* 得到文件的绝对路径
*
* @param identifier
* @param filename
* @return
*/
private String getFilePath(String identifier, String filename) {
String ext = filename.substring(filename.lastIndexOf("."));
// retu
java+vue断点续传核心代码
需积分: 0 101 浏览量
更新于2023-08-29
收藏 11KB ZIP 举报
在IT行业中,断点续传是一项非常实用的技术,尤其在大文件传输时,它能显著提升用户体验并提高网络效率。本话题将详细讲解基于Java后端和Vue.js前端实现的断点续传的核心代码和原理。
让我们理解断点续传的基本概念。断点续传是指在文件传输过程中,如果因为网络中断或其他原因导致传输失败,用户可以在同一位置(即上次断开的“断点”)重新开始传输,而不是从头开始。这大大减少了在网络不稳定环境下的文件传输时间。
Java作为后端处理的主要职责是提供文件分块、接收上传的文件块以及组合这些块以恢复完整文件。Vue.js作为前端框架,主要用于用户交互界面的展示,控制文件的选取、分块、上传以及进度显示。
1. **Java服务端**:
- **服务初始化**:Java后端需要设置一个接口,用于处理文件的分块请求,获取文件大小并确定分块大小。
- **文件分块**:根据文件大小,将文件分割成多个小块,每个块都有自己的标识,便于后续匹配。
- **存储分块**:将接收到的每个文件块存储到服务器的临时目录,同时记录每个块的状态(是否已接收完整)。
- **组合文件**:当所有块都上传完毕,按照标识顺序将这些块组合成原始文件。
2. **Vue.js前端**:
- **文件选择**:使用HTML5的File API,让用户选择要上传的文件,并获取文件大小。
- **文件分块**:前端也需要将文件分块,这里可以采用Blob对象的slice方法,设定分块大小与后端保持一致。
- **上传管理**:维护一个状态表,跟踪每个块的上传状态,并调用API逐个上传。
- **断点记录**:在本地持久化当前的上传进度,包括已上传的块和每个块的上传状态,以便于断点续传。
- **进度反馈**:更新用户界面的进度条,显示当前的上传进度。
在压缩包中的文件可能包含以下内容:
- `result`:可能包含后端处理结果的接口定义和实现,如文件上传成功后的响应。
- `service`:Java的服务层代码,处理文件的分块、接收和组合逻辑。
- `controller`:Java的控制器层,负责处理HTTP请求,与前端进行通信。
- `js`:前端的JavaScript代码,实现Vue.js应用的逻辑,包括文件选择、分块、上传和断点续传功能。
- `dto`:数据传输对象(Data Transfer Object),在Java和Vue.js之间交换数据的模型类。
通过这些核心代码,我们可以实现一个完整的断点续传系统。在实际项目中,还需要考虑错误处理、安全性、性能优化等问题,例如文件的MD5校验以确保完整性,以及使用HTTPS保证数据传输的安全性。断点续传技术的应用,使得大文件上传在现代Web应用中变得更为流畅和可靠。

婲落ヽ紅顏誶
- 粉丝: 67
- 资源: 10
最新资源
- 精品推荐-2024 ChatGPT大模型技术场景与商业应用视频精讲合集(45课).zip
- 苏苏源码-springboot416-农产品电子商务网站(编号:34074366).zip
- 直流电机双闭环调速系统仿真与详细设计报告:转速稳定,灵活调节,适应多变负载与电网电压环境,直流电机双闭环调速系统仿真与详细设计报告:转速稳定,灵活调节,适应多变负载与电网电压环境,直流电机双闭环(电流
- Java Web开发中使用HttpServletRequestWrapper实现可重复读取请求的技术方案
- 英飞凌电源反极性保护电路分析
- 基于强化学习算法Q-learning的水库优化调度研究-探索Python代码实现与优化策略,强化学习驱动的水库优化调度策略研究:基于Q-learning算法的智能决策与实施路径,python代码-基
- 近邻传播聚类算法(AP算法)的MATLAB实现:无需预设聚类数目与中心,便捷高准确度的操作方法 ,近邻传播聚类算法(AP算法)的Matlab实现:无需预设聚类数目与中心,便捷操作,高准确度,新颖方法探
- 基于MATLAB与CPLEX求解器的电转气协同碳捕集虚拟电厂优化调度策略研究,基于MATLAB与CPLEX求解器的电转气协同碳捕集虚拟电厂优化调度研究,MATLAB代码:计及电转气协同的含碳捕集与垃圾
- PSO算法优化SVM模型在时间序列预测分析中的应用:代码详解与数据替换指南,详细注释的SVM时间序列预测分析:PSO优化算法,高效通用代码模板,直接替换数据即运行,PSO优化SVM做时间序列预测分析
- COMSOL激光打孔与水平集两相流仿真模型探究:温度场与流场一体化分析的两个版本介绍,COMSOL激光打孔与水平集两相流仿真模型:温度场流场深度解析,双版本探究,comsol激光打孔(不通)水平集两相
- 异步电机故障仿真研究:定子绕组匝间短路与转子断条故障的模拟分析,异步电机定子绕组匝间短路与转子断条故障仿真分析:探究感应电机匝间短路故障的机理与影响,异步电机定子绕组匝间短路仿真,转子断条故障仿真,感
- 日前日内两阶段调度综合能源分析:基于Matlab与Yalmip的程序优化结果对比及成本评估,日前日内两阶段调度综合能源分析:基于Matlab与Yalmip的程序优化结果对比及机组成本与弃风惩罚的探讨
- 基于PLC的智能农业温室大棚控制系统的电气控制方案 包括梯形图程序、接线图及原理图设计、IO分配与组态画面展示,基于PLC的智能农业温室大棚控制:电气控制组态、梯形图程序与画面组态详解,基于PLC的智
- 机器人研究数模实验方案:基于LQR控制的仿真优化与测试,适用于机械臂、无人机等镇定与轨迹追踪控制算法,通过Simulink平台快速搭建仿真平台,机器人研究Simulink平台自动化搭建与测试方案:LQ
- 基于正向开发的P2混合动力轿车并联模型:先算整车阻力再精准分配扭矩,P2轿车并联模型:正向开发下的整车阻力计算与扭矩分配策略,P2轿车并联模型,完全基于正向开发,先计算整车阻力,然后根据当前车辆模式进
- 混合储能系统能量管理Simulink仿真模型:蓄电池与超级电容协同控制策略及SOC限值管理,混合储能系统能量管理Simulink仿真模型研究:蓄电池与超级电容的功率分配与控制策略,混合储能系统能量管理