From cccfb7bd867e6cdf0d03c59c96866cbcf74c63e7 Mon Sep 17 00:00:00 2001 From: rdpnr_puzhi <13060209078@163.com> Date: Tue, 12 Aug 2025 19:14:17 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E5=90=8E=E7=AB=AF):=20=E8=81=94=E8=B0=83?= =?UTF-8?q?=E4=BF=AE=E6=94=B92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/config/RestTemplateConfig.java | 20 ++++++ .../controller/FileChunkController.java | 72 ++++++++++++++++--- .../project/controller/TestController.java | 10 +-- .../unisinsight/project/entity/dao/Image.java | 2 +- .../impl/DeviceUserMappingServiceImpl.java | 6 +- .../service/impl/ImageServiceImpl.java | 31 +++++++- 6 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 nex-be/src/main/java/com/unisinsight/project/config/RestTemplateConfig.java diff --git a/nex-be/src/main/java/com/unisinsight/project/config/RestTemplateConfig.java b/nex-be/src/main/java/com/unisinsight/project/config/RestTemplateConfig.java new file mode 100644 index 0000000..e5df6fa --- /dev/null +++ b/nex-be/src/main/java/com/unisinsight/project/config/RestTemplateConfig.java @@ -0,0 +1,20 @@ +package com.unisinsight.project.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.client.RestTemplate; + +/** + * @description: + * @author: rdpnr_puzhi + * @create: 2025/08/12 + */ + +@Configuration +public class RestTemplateConfig { + + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} diff --git a/nex-be/src/main/java/com/unisinsight/project/controller/FileChunkController.java b/nex-be/src/main/java/com/unisinsight/project/controller/FileChunkController.java index 05b2f9d..2faa48d 100644 --- a/nex-be/src/main/java/com/unisinsight/project/controller/FileChunkController.java +++ b/nex-be/src/main/java/com/unisinsight/project/controller/FileChunkController.java @@ -1,5 +1,8 @@ package com.unisinsight.project.controller; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.json.JSONUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.unisinsight.project.entity.dao.Image; import com.unisinsight.project.exception.BaseErrorCode; import com.unisinsight.project.exception.Result; @@ -10,8 +13,11 @@ import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.ObjectUtils; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import org.springframework.web.client.RestTemplate; import org.springframework.web.multipart.MultipartFile; import javax.annotation.Resource; @@ -21,10 +27,8 @@ import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; /** @@ -44,6 +48,14 @@ public class FileChunkController { @Value("${file.upload.dir:${user.home}/uploads}") private String uploadDir; + // 请求bt配置 + @Value("${file.upload.bt-url}") + private String btUrl; + + @Resource + private RestTemplate restTemplate; + + // 存储每个文件的分片信息 private final Map fileUploadMap = new ConcurrentHashMap<>(); @@ -95,13 +107,20 @@ public class FileChunkController { log.info("分片文件md5校验失败,chunkMd5:{},md5:{}", chunkMd5, md5); throw new RuntimeException("分片文件md5校验失败"); } + QueryWrapper wrapper = new QueryWrapper<>(); + wrapper.lambda().eq(Image::getImageName, fileName); + List imageList = imageMapper.selectList(wrapper); + if (CollectionUtil.isNotEmpty(imageList)) { + response.put("success", false); + response.put("status", "error"); + response.put("message", "当前文件已经上传"); + return ResponseEntity.status(200).body(response); + } // 创建临时目录 Path fileTempDir = Paths.get(tempDir, fileId); if (!Files.exists(fileTempDir)) { Files.createDirectories(fileTempDir); } - log.info("创建临时目录: {}", fileTempDir); - log.info("上传分片文件: {}", fileName); // 保存分片文件 String chunkFileName = String.format("%05d.part", chunkNumber); log.info("保存分片文件: {}", chunkFileName); @@ -120,9 +139,8 @@ public class FileChunkController { if (!Files.exists(finalDir)) { Files.createDirectories(finalDir); } - log.info("合并文件: {}", finalDir); Path finalFilePath = finalDir.resolve(fileName); - log.info("合并所有分片文件: {}", finalFilePath.getFileName()); + log.info("合并所有分片文件: {}", finalFilePath); mergeChunks(fileId, finalFilePath, totalChunks); // 清理临时文件 @@ -134,11 +152,41 @@ public class FileChunkController { Image image = new Image(); image.setImageName(fileName); - image.setStoragePath(uploadDir); + image.setStoragePath(String.valueOf(finalFilePath)); image.setImageStatus(1); int insert = imageMapper.insert(image); log.info("镜像新增insert:{}", insert); if (insert == 1) { + + // 异步执行创建和做种操作 + CompletableFuture.runAsync(() -> { + try { + String url = btUrl + "/test/start?sourceFile=%s&torrentFile=%s"; + url = String.format(url, finalFilePath, finalFilePath + ".torrent"); + log.info("请求bt创建接口参数: {}", url); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, Boolean.class); + log.info("请求bt创建接口返回: {}", JSONUtil.toJsonStr(responseEntity)); + HttpStatus statusCode = responseEntity.getStatusCode(); + if (statusCode != HttpStatus.OK) { + boolean result = Boolean.TRUE.equals(responseEntity.getBody()); + if (result) { + log.info("请求bt创建接口成功"); + QueryWrapper imageQueryWrapper = new QueryWrapper<>(); + imageQueryWrapper.lambda().eq(Image::getImageName, fileName); + Image imageBt = imageMapper.selectOne(imageQueryWrapper); + if (ObjectUtils.isNotEmpty(imageBt)) { + imageBt.setBtPath(finalFilePath + ".torrent"); + int update = imageMapper.updateById(imageBt); + log.info("镜像bt更新insert:{}", update); + } else { + log.info("镜像bt更新查询失败:{}", fileName); + } + } + } + } catch (Exception e) { + log.error("请求bt创建接口失败: {}", e.getMessage(), e); + } + }); response.put("status", "completed"); response.put("message", "文件上传并合并完成"); response.put("filePath", finalFilePath.toString()); @@ -159,8 +207,8 @@ public class FileChunkController { } catch (Exception e) { response.put("success", false); response.put("status", "error"); - response.put("message", "上传失败: " + e.getMessage()); - log.info("上次失败清理临时文件: {}", fileId); + response.put("message", "上传失败"); + log.info("上次失败清理临时文件: {},error:{}", fileId, e.getMessage(), e); try { cleanupTempFiles(fileId); cleanUploadFile(fileName); @@ -179,6 +227,8 @@ public class FileChunkController { } log.info("取消上传,清理临时文件: {}", fileId); try { + // 从上传映射中移除 + fileUploadMap.remove(fileId); cleanupTempFiles(fileId); } catch (IOException ex) { log.error("清理临时文件失败,fileId:{}, {}", fileId, ex.getMessage(), ex); diff --git a/nex-be/src/main/java/com/unisinsight/project/controller/TestController.java b/nex-be/src/main/java/com/unisinsight/project/controller/TestController.java index 76d6f5e..442a614 100644 --- a/nex-be/src/main/java/com/unisinsight/project/controller/TestController.java +++ b/nex-be/src/main/java/com/unisinsight/project/controller/TestController.java @@ -2,21 +2,21 @@ package com.unisinsight.project.controller; import com.unisinsight.project.util.BtTorrentUtils; import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; - -import java.io.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; @RestController("test") @Slf4j public class TestController { @GetMapping("/start") - public String start(@RequestParam("sourceFile") String sourceFile, + public boolean start(@RequestParam("sourceFile") String sourceFile, @RequestParam("torrentFile") String torrentFile) { System.out.println("开始做种..."); boolean seedResult = BtTorrentUtils.createAndSeed(sourceFile, torrentFile); System.out.println("做种结果: " + (seedResult ? "成功" : "失败")); - return "success"; + return seedResult; } @GetMapping("/stop") diff --git a/nex-be/src/main/java/com/unisinsight/project/entity/dao/Image.java b/nex-be/src/main/java/com/unisinsight/project/entity/dao/Image.java index 8043c98..24a5af9 100644 --- a/nex-be/src/main/java/com/unisinsight/project/entity/dao/Image.java +++ b/nex-be/src/main/java/com/unisinsight/project/entity/dao/Image.java @@ -32,7 +32,7 @@ public class Image implements Serializable { private Integer imageType; /** - * 镜像状态: 1-成功,2-失败 + * 镜像状态: 1-成功,2-失败,3-做种中 */ @TableField(value = "image_status") private Integer imageStatus; diff --git a/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceUserMappingServiceImpl.java b/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceUserMappingServiceImpl.java index 2e78f88..8e9dcaf 100644 --- a/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceUserMappingServiceImpl.java +++ b/nex-be/src/main/java/com/unisinsight/project/service/impl/DeviceUserMappingServiceImpl.java @@ -154,8 +154,10 @@ public class DeviceUserMappingServiceImpl extends ServiceImpl @Resource private ImageMapper imageMapper; + // 请求bt配置 + @Value("${file.upload.bt-url}") + private String btUrl; + + @Resource + private RestTemplate restTemplate; + + @Override public Result selectPage(ImageReq imageReq) { Page page = new Page<>(imageReq.getPageNum(), imageReq.getPageSize()); @@ -67,7 +80,23 @@ public class ImageServiceImpl extends ServiceImpl Image image = imageMapper.selectById(deleteIdReq.getId()); if (ObjectUtils.isNotEmpty(image)) { try { - Path filePath = Paths.get(image.getStoragePath(), image.getImageName()); + try { + String url = btUrl + "/test/stop?sourceFile=%s"; + url = String.format(url, image.getStoragePath()); + log.info("请求bt停止接口参数: {}", url); + ResponseEntity responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, Boolean.class); + log.info("请求bt停止接口返回: {}", JSONUtil.toJsonStr(responseEntity)); + HttpStatus statusCode = responseEntity.getStatusCode(); + if (statusCode != HttpStatus.OK) { + boolean result = responseEntity.getBody(); + if (result) { + log.info("请求bt停止接口成功"); + } + } + } catch (Exception e) { + log.error("请求bt停止接口失败: {}", e.getMessage(), e); + } + Path filePath = Paths.get(image.getStoragePath()); if (Files.exists(filePath)) { // 删除文件 Files.delete(filePath);