package com.ud.uploadfiledemo.controller;
import com.ud.uploadfiledemo.model.ChunkInfo;
import com.ud.uploadfiledemo.model.MergeStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import java.time.Instant;
import java.io.*;
import java.nio.channels.FileChannel;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@RestController
@RequestMapping("/upload")
@Slf4j
public class FileUploadController {
@Value("${file.upload.path}")
private String uploadPath;
// 用于记录正在合并的文件
private final Set<String> mergingFiles = ConcurrentHashMap.newKeySet();
// 用于存储合并进度的Map
private final ConcurrentHashMap<String, MergeStatus> mergeStatusMap = new ConcurrentHashMap<>();
private final TaskScheduler taskScheduler;
public FileUploadController() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.initialize();
this.taskScheduler = scheduler;
}
@PostMapping("/chunk")
public ResponseEntity<String> uploadChunk(@RequestParam("file") MultipartFile file,
ChunkInfo chunkInfo) {
try {
// 创建文件块存储目录
String chunkDirPath = uploadPath + File.separator + chunkInfo.getFileMd5();
File chunkDir = new File(chunkDirPath);
if (!chunkDir.exists()) {
chunkDir.mkdirs();
}
// 写入文件块
File chunkFile = new File(chunkDirPath + File.separator + chunkInfo.getChunkNumber());
file.transferTo(chunkFile);
// 检查是否所有块都已上传,并且当前文件不在合并过程中
if (checkIfAllChunksUploaded(chunkInfo) && !mergingFiles.contains(chunkInfo.getFileMd5())) {
try {
// 标记文件正在合并
mergingFiles.add(chunkInfo.getFileMd5());
mergeChunks(chunkInfo);
} finally {
// 合并完成后移除标记
mergingFiles.remove(chunkInfo.getFileMd5());
}
}
return ResponseEntity.ok("Chunk uploaded successfully");
} catch (IOException e) {
log.error("Upload chunk failed", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Upload failed: " + e.getMessage());
}
}
@GetMapping("/chunk/verify")
public ResponseEntity<Set<Integer>> verifyChunk(@RequestParam String fileMd5) {
// 获取已上传的块号
Set<Integer> uploadedChunks = getUploadedChunks(fileMd5);
return ResponseEntity.ok(uploadedChunks);
}
private Set<Integer> getUploadedChunks(String fileMd5) {
Set<Integer> uploadedChunks = new HashSet<>();
File chunkDir = new File(uploadPath + File.separator + fileMd5);
if (chunkDir.exists()) {
File[] files = chunkDir.listFiles();
if (files != null) {
for (File file : files) {
uploadedChunks.add(Integer.parseInt(file.getName()));
}
}
}
return uploadedChunks;
}
private boolean checkIfAllChunksUploaded(ChunkInfo chunkInfo) {
Set<Integer> uploadedChunks = getUploadedChunks(chunkInfo.getFileMd5());
return uploadedChunks.size() == chunkInfo.getTotalChunks();
}
@GetMapping("/merge/status")
public ResponseEntity<MergeStatus> getMergeStatus(@RequestParam String fileMd5) {
MergeStatus status = mergeStatusMap.getOrDefault(fileMd5, new MergeStatus("waiting", 0));
return ResponseEntity.ok(status);
}
private void mergeChunks(ChunkInfo chunkInfo) throws IOException {
String fileMd5 = chunkInfo.getFileMd5();
try {
mergeStatusMap.put(fileMd5, new MergeStatus("merging", 0));
String chunkDirPath = uploadPath + File.separator + fileMd5;
// 处理文件路径,替换非法字符
String sanitizedFilename = chunkInfo.getFilename().replace('\\', '/');
Path targetPath = Paths.get(uploadPath, sanitizedFilename);
File targetDir = targetPath.getParent().toFile();
if (!targetDir.exists()) {
targetDir.mkdirs();
}
log.info("Merging chunks to: {}", targetPath);
// 获取总块数用于计算进度
int totalChunks = chunkInfo.getTotalChunks();
int processedChunks = 0;
try (FileChannel outChannel = new FileOutputStream(targetPath.toFile()).getChannel()) {
for (int i = 1; i <= totalChunks; i++) {
File chunk = new File(chunkDirPath + File.separator + i);
if (!chunk.exists()) {
throw new IOException("Chunk file missing: " + i);
}
try (FileChannel inChannel = new FileInputStream(chunk).getChannel()) {
inChannel.transferTo(0, inChannel.size(), outChannel);
}
processedChunks++;
// 更新合并进度
int progress = (processedChunks * 100) / totalChunks;
mergeStatusMap.put(fileMd5, new MergeStatus("merging", progress));
}
}
// 删除临时文件
try {
Thread.sleep(100);
deleteChunkDir(new File(chunkDirPath));
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
log.warn("Interrupted while waiting to delete chunks", e);
}
// 更新状态为完成
mergeStatusMap.put(fileMd5, new MergeStatus("completed", 100));
log.info("File merged successfully: {}", targetPath);
} catch (Exception e) {
mergeStatusMap.put(fileMd5, new MergeStatus("error", 0, e.getMessage()));
throw e;
} finally {
// 5秒后清除状态
taskScheduler.schedule(
() -> mergeStatusMap.remove(fileMd5),
Instant.now().plusSeconds(5)
);
}
}
private void deleteChunkDir(File dir) {
if (dir.exists()) {
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (!file.delete()) {
log.warn("Failed to delete chunk file: {}", file.getAbsolutePath());
}
}
}
if (!dir.delete()) {
log.warn("Failed to delete chunk directory: {}", dir.getAbsolutePath());
}
}
}
}
没有合适的资源?快使用搜索试试~ 我知道了~
JAVA大文件上传源代码
共23个文件
java:7个
xml:6个
gitignore:2个
需积分: 0 1 下载量 71 浏览量
更新于2025-01-15
收藏 35KB ZIP 举报
JAVA 大文件上传项目
## 项目简介
这是一个基于 Spring Boot 实现的大文件分片上传项目,支持断点续传、文件秒传、实时进度显示等功能。项目采用前后端分离架构,使用 Thymeleaf 作为模板引擎,实现了高效可靠的文件上传功能。
## 核心特性
1. **分片上传**
- 自动将大文件切分为2MB大小的分片
- 支持并发上传多个分片
- 实时显示上传进度
2. **断点续传**
- 记录已上传分片信息
- 支持暂停/继续上传
- 网络中断自动恢复
3. **文件秒传**
- 基于MD5文件指纹
- 秒级验证文件是否已存在
- 避免重复上传
4. **实时进度**
- 双进度条设计(上传进度和合并进度)
- 精确的时间统计
- 友好的状态提示
## 技术栈
- 后端:Spring Boot 3.4.1
- 前端:HTML5 + JavaScript
- 模板引擎:Thymeleaf
- 文件处理:Apache Commons IO
- 构建工具:Maven
收起资源包目录
uploadfiledemo.zip (23个子文件)
uploadfiledemo
HELP.md 1KB
mvnw.cmd 7KB
pom.xml 3KB
.gitattributes 38B
src
test
java
com
ud
uploadfiledemo
UploadfiledemoApplicationTests.java 230B
main
resources
templates
upload.html 34KB
static
js
spark-md5.min.js 10KB
application.yml 162B
java
com
ud
uploadfiledemo
controller
FileUploadController.java 7KB
PageController.java 289B
model
ChunkInfo.java 463B
MergeStatus.java 390B
UploadfiledemoApplication.java 342B
config
UploadConfig.java 1KB
.mvn
wrapper
maven-wrapper.properties 951B
.idea
jarRepositories.xml 2KB
workspace.xml 5KB
misc.xml 539B
compiler.xml 1KB
.gitignore 190B
encodings.xml 191B
mvnw 10KB
.gitignore 395B
共 23 条
- 1
资源推荐
资源预览
资源评论
128 浏览量
2023-05-29 上传
191 浏览量
2024-01-05 上传
5星 · 资源好评率100%
146 浏览量
111 浏览量
107 浏览量
2013-02-20 上传
175 浏览量
2020-03-03 上传
177 浏览量
2018-01-19 上传
175 浏览量
2023-08-04 上传
117 浏览量
5星 · 资源好评率100%
149 浏览量
2023-10-16 上传
5星 · 资源好评率100%
165 浏览量
2008-10-15 上传
2019-07-10 上传
5星 · 资源好评率100%
194 浏览量
120 浏览量
2012-11-26 上传
2023-03-12 上传
资源评论
- #完美解决问题
- #运行顺畅
- #内容详尽
- #全网独家
- #注释完整
熊文豪
- 粉丝: 652
- 资源: 6
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- 图书管理系统(C语言版)
- 资源搬运:latent-gt-code512.pth Apr 17, 2023
- DeepSeek-R1技术报告
- epic pen屏幕笔非常好用!
- 电机控制单电阻采样方法及电流重构解析,PWM移项技术与双、三电阻应用研究,电机控制单电阻采样方法详细资料,电流重构,pwm移项方法等等 还有双电阻和三电阻都噢 ,电机控制;单电阻采样方法;电流重构
- 大模型与LangChain4j:Java环境下构建和优化AI应用的全方位指南
- 遗传算法在微电网经济运行优化中的应用(含蓄电池、风电等能源)-matlab程序解析,遗传算法的微电网经济运行优化(matlab程序) 采用遗传算法的优化运行 在微电网中 系统中包括蓄电池、风电、柴油机
- 双碳目标下综合能源系统优化调度策略:分时机制、碳交易与双层响应优化的模型分析(Matlab+Yalmip+Cplex求解),分时优化机制+碳交易+双层需求响应优化+综合能源系统IES联合低碳优化调度:
- Node.js接收文件分片数据并进行合并处理
- 基于 DeepSeek 的情感分析的 Python 源码
- 软考报考流程指南-2024.pdf
- 基于Matlab的含碳捕集与电转气协同虚拟电厂优化调度策略求解程序,《计及电转气协同的含碳捕集与垃圾焚烧电厂优化调度》matlab程序 #电转气协同、碳捕集、电厂优化调度# matlab程序,采用y
- 基于Tent映射的混合灰狼优化算法:结合混沌初始种群与非线性控制参数的改进策略,一种基于Tent映射的混合灰狼优化的改进算法-滕志军 MATLAB代码,可提供代码与lunwen 首先,其通过 Te
- UMP Pro Win Mac Linux WebGL(2.0.3修改版)
- Springboot+vue3在线预约系统
- 博图程序块与西门子PLC在水处理中的智能电机控制:一键启动,轮询运行与最短运行时间筛选,博图程序块,西门子plc程序 做水处理时,会用到多个电机,但是运行时只启动其中几台电机,其他的备用,现在程序块
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功