news 2026/1/17 7:06:18

基于spring web实现简单分片上传demo

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
基于spring web实现简单分片上传demo

基于spring web实现简单分片上传demo

我的场景:调用三方系统一直…报错,为了了解分片,自己写一个分片、从而大概猜测是谁的问题。

controller

import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.nio.file.*; import java.util.Arrays; import java.util.Comparator; import java.util.stream.Stream; // 必须导入这个 @RestController @RequestMapping("/upload") @CrossOrigin public class ChunkUploadController { private static final String UPLOAD_DIR = "upload_data"; @PostMapping("/chunk") public String uploadChunk(@RequestParam("file") MultipartFile file, @RequestParam("chunkNumber") int chunkNumber, @RequestParam("totalChunks") int totalChunks, @RequestParam("identifier") String identifier, @RequestParam("filename") String filename) throws IOException { // 【关键修改】使用 toAbsolutePath() 获取绝对路径 Path chunkDir = Paths.get(UPLOAD_DIR, "temp", identifier).toAbsolutePath(); if (!Files.exists(chunkDir)) { Files.createDirectories(chunkDir); } Path chunkPath = chunkDir.resolve("chunk-" + chunkNumber); file.transferTo(chunkPath.toFile()); System.out.println("收到分片: " + chunkNumber + " / " + totalChunks); if (isAllChunksUploaded(chunkDir, totalChunks)) { return mergeChunks(chunkDir, filename, identifier); } return "Chunk " + chunkNumber + " uploaded."; } /** * JDK 8 修正版:将 var 替换为 Stream<Path> */ private boolean isAllChunksUploaded(Path chunkDir, int totalChunks) { // Files.list 返回的是 Stream<Path>,且实现了 AutoCloseable,需要 try-with-resources 关闭流 try (Stream<Path> list = Files.list(chunkDir)) { return list.count() == totalChunks; } catch (IOException e) { return false; } } private String mergeChunks(Path chunkDir, String filename, String identifier) throws IOException { System.out.println("所有分片已接收,开始合并..."); Path targetFile = Paths.get(UPLOAD_DIR, "final", identifier + "_" + filename); if (!Files.exists(targetFile.getParent())) { Files.createDirectories(targetFile.getParent()); } File[] chunks = chunkDir.toFile().listFiles(); if (chunks != null) { // JDK 8 写法:Comparator Arrays.sort(chunks, new Comparator<File>() { @Override public int compare(File o1, File o2) { String name1 = o1.getName(); String name2 = o2.getName(); int n1 = Integer.parseInt(name1.substring(name1.lastIndexOf("-") + 1)); int n2 = Integer.parseInt(name2.substring(name2.lastIndexOf("-") + 1)); return Integer.compare(n1, n2); } }); // 合并文件 try (OutputStream out = new BufferedOutputStream(Files.newOutputStream(targetFile, StandardOpenOption.CREATE, StandardOpenOption.APPEND))) { for (File chunk : chunks) { Files.copy(chunk.toPath(), out); } } } System.out.println("合并完成: " + targetFile.toString()); // 合并完成后清理临时目录(可选) // deleteDirectory(chunkDir.toFile()); return "File uploaded and merged successfully: " + targetFile.toString(); } }

调用客户端client代码

import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import java.io.*; import java.util.UUID; public class ChunkUploadClient { private static final String UPLOAD_URL = "http://localhost:8080/upload/chunk"; private static final int CHUNK_SIZE = 20 * 1024 * 1024; // 定义分片大小: 2MB public static void main(String[] args) { // 1. 准备要上传的文件 (请修改为你电脑上真实存在的文件路径) File sourceFile = new File("C:\\Users\\qinqin\\Desktop\\附件落地测试\\test.zip"); if (!sourceFile.exists()) { System.err.println("测试文件不存在,请修改路径!"); return; } // 2. 计算总分片数 long fileSize = sourceFile.length(); int totalChunks = (int) Math.ceil((double) fileSize / CHUNK_SIZE); // 生成一个唯一标识,代表这次上传任务 String identifier = UUID.randomUUID().toString(); String filename = sourceFile.getName(); System.out.println("开始上传文件: " + filename); System.out.println("文件大小: " + fileSize + " bytes, 总分片数: " + totalChunks); RestTemplate restTemplate = new RestTemplate(); // 3. 开始切片并上传 try (RandomAccessFile raf = new RandomAccessFile(sourceFile, "r")) { byte[] buffer = new byte[CHUNK_SIZE]; int bytesRead; int chunkNumber = 1; while ((bytesRead = raf.read(buffer)) != -1) { // 如果是最后一片,可能不满CHUNK_SIZE,需要截取 if (bytesRead < CHUNK_SIZE) { byte[] temp = new byte[bytesRead]; System.arraycopy(buffer, 0, temp, 0, bytesRead); uploadChunk(restTemplate, temp, chunkNumber, totalChunks, identifier, filename); } else { uploadChunk(restTemplate, buffer, chunkNumber, totalChunks, identifier, filename); } chunkNumber++; } } catch (Exception e) { e.printStackTrace(); } } private static void uploadChunk(RestTemplate restTemplate, byte[] fileBytes, int chunkNumber, int totalChunks, String identifier, String filename) throws IOException { // 构造临时文件用于封装进 MultipartFile (RestTemplate 需要 Resource) // 在实际生产的Client中,通常不需要落盘,直接用 ByteArrayResource, // 但为了模拟名为"file"的文件流,这里创建临时文件是最通用的做法 File tempChunkFile = File.createTempFile("client-chunk-", ".tmp"); try (FileOutputStream fos = new FileOutputStream(tempChunkFile)) { fos.write(fileBytes); } // 构造请求体 MultiValueMap<String, Object> body = new LinkedMultiValueMap<>(); body.add("file", new FileSystemResource(tempChunkFile)); body.add("chunkNumber", chunkNumber); body.add("totalChunks", totalChunks); body.add("identifier", identifier); body.add("filename", filename); HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.MULTIPART_FORM_DATA); HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(body, headers); // 发送请求 String response = restTemplate.postForObject(UPLOAD_URL, requestEntity, String.class); System.out.println("分片 " + chunkNumber + " 响应: " + response); // 清理客户端临时文件 tempChunkFile.delete(); } }
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/1/15 22:41:00

用ABAQUS探索建筑结构抗震的奥秘

ABAQUS抗震&#xff0c;可建模&#xff0c;可调模型&#xff0c;钢框架及混凝土框架抗震时程分析&#xff0c;模态分析&#xff0c;连接器模拟线性非线性滞回曲线&#xff0c;接触设置。在建筑结构的抗震设计领域&#xff0c;ABAQUS是一款强大的分析工具&#xff0c;它能够帮助…

作者头像 李华
网站建设 2026/1/15 8:18:46

【计算机毕业设计案例】基于SpringBoot+Vue的高校志愿活动管理系统的设计与实现志愿者活动组织宣传管理系统(程序+文档+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/1/14 19:12:42

烦心之烦心

最近有点乱&#xff0c;无心写作&#xff01;窗外的雨滴轻轻敲打着玻璃&#xff0c;仿佛在演奏一首宁静的乐曲。雨声渐渐淹没了内心的嘈杂&#xff0c;让思绪得以沉淀。在这样的时刻&#xff0c;不妨放下手中的事务&#xff0c;静静聆听自然的声音&#xff0c;感受雨滴带来的清…

作者头像 李华
网站建设 2026/1/14 4:47:02

【ComfyUI错误】【SmoothMixWan22工作流】wanblockswap节点不显示解决方法

【ComfyUI错误】【SmoothMixWan22工作流】wanblockswap节点不显示解决方法 使用《 Work-Fisher:Wan2.2:Smooth Mix I2V合集》作者的工作流的时候&#xff0c;本地和runninghub上均无法显示wanblockswap的参数设置。 先说结论&#xff1a;如果无法显示可以直接删除或者不使用这…

作者头像 李华
网站建设 2026/1/16 22:41:58

(新卷,100分)- 查找众数及中位数(Java JS Python C)

(新卷,100分)- 查找众数及中位数&#xff08;Java & JS & Python & C&#xff09;题目描述众数是指一组数据中出现次数量多的那个数&#xff0c;众数可以是多个。中位数是指把一组数据从小到大排列&#xff0c;最中间的那个数&#xff0c;如果这组数据的个数是奇数…

作者头像 李华