diff --git a/nex-be/src/main/java/com/unisinsight/project/config/WebMvcConfig.java b/nex-be/src/main/java/com/unisinsight/project/config/WebMvcConfig.java new file mode 100644 index 0000000..2c9f96c --- /dev/null +++ b/nex-be/src/main/java/com/unisinsight/project/config/WebMvcConfig.java @@ -0,0 +1,14 @@ +package com.unisinsight.project.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebMvcConfig implements WebMvcConfigurer { + + public void addResourceHandlers(ResourceHandlerRegistry registry) { + WebMvcConfigurer.super.addResourceHandlers(registry); + registry.addResourceHandler("/api/vdi/file/down/**").addResourceLocations("file:/var/lib/vdi/test/"); + } +} 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 bc736d8..76d6f5e 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 @@ -1,5 +1,6 @@ package com.unisinsight.project.controller; +import com.unisinsight.project.util.BtTorrentUtils; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; @@ -9,103 +10,20 @@ import java.io.*; @Slf4j public class TestController { - - private static final String BT_SCRIPT_PATH = "/var/lib/vdi/nodejs/bttorrent.sh"; - private static final String RUN_USER = "java_usr"; // 替换为你的实际用户 - - /** - * 制作种子并开始做种 - * - * @param sourceFile 源文件路径(如 /data/file.iso) - * @param torrentFile 生成的种子文件路径(如 /data/file.torrent) - * @return 是否成功 - */ - public static boolean createAndSeed(String sourceFile, String torrentFile) { - // 验证文件是否存在 - if (!new File(sourceFile).exists()) { - System.err.println("源文件不存在: " + sourceFile); - return false; - } - // 确保目标目录存在 - new File(torrentFile).getParentFile().mkdirs(); - return executeCommand("start", sourceFile, torrentFile); - } - - /** - * 停止指定文件的做种 - * - * @param sourceFile 源文件路径(如 /data/file.iso) - * @return 是否成功 - */ - public static boolean stopSeeding(String sourceFile) { - return executeCommand("stop_path", sourceFile, null); - } - - /** - * 执行 bttorrent.sh 脚本 - */ - private static boolean executeCommand(String command, String arg1, String arg2) { - try { - // 构造命令 - ProcessBuilder pb = new ProcessBuilder( - "sudo", "-u", RUN_USER, - BT_SCRIPT_PATH, - command, - arg1, - arg2 != null ? arg2 : "" // 处理可选参数 - ).redirectErrorStream(true); - // 启动进程 - Process process = pb.start(); - // 打印输出(调试用) - logProcessOutput(process); - // 等待执行完成 - return process.waitFor() == 0; - } catch (IOException | InterruptedException e) { - e.printStackTrace(); - return false; - } - } - - /** - * 打印进程输出 - */ - private static void logProcessOutput(Process process) throws IOException { - try (BufferedReader reader = new BufferedReader( - new InputStreamReader(process.getInputStream()))) { - String line; - while ((line = reader.readLine()) != null) { - System.out.println("[BT] " + line); - } - } - } - // 测试用例 - public static void main(String[] args) { - String sourceFile = "/var/lib/vdi/test/example.iso"; - String torrentFile = "/var/lib/vdi/test/example.torrent"; - // 测试做种 - System.out.println("开始做种..."); - boolean seedResult = createAndSeed(sourceFile, torrentFile); - System.out.println("做种结果: " + (seedResult ? "成功" : "失败")); - // 测试停止 - System.out.println("停止做种..."); - boolean stopResult = stopSeeding(sourceFile); - System.out.println("停止结果: " + (stopResult ? "成功" : "失败")); - } - @GetMapping("/start") - public String start(@RequestParam("sourceFile")String sourceFile, - @RequestParam("torrentFile")String torrentFile) { + public String start(@RequestParam("sourceFile") String sourceFile, + @RequestParam("torrentFile") String torrentFile) { System.out.println("开始做种..."); - boolean seedResult = createAndSeed(sourceFile, torrentFile); + boolean seedResult = BtTorrentUtils.createAndSeed(sourceFile, torrentFile); System.out.println("做种结果: " + (seedResult ? "成功" : "失败")); return "success"; } @GetMapping("/stop") - public String stop(@RequestParam("sourceFile")String sourceFile) { + public String stop(@RequestParam("sourceFile") String sourceFile) { // 测试停止 System.out.println("停止做种..."); - boolean stopResult = stopSeeding(sourceFile); + boolean stopResult = BtTorrentUtils.stopSeeding(sourceFile); System.out.println("停止结果: " + (stopResult ? "成功" : "失败")); return "success"; } diff --git a/nex-be/src/main/java/com/unisinsight/project/util/BtTorrentUtil.java b/nex-be/src/main/java/com/unisinsight/project/util/BtTorrentUtil.java deleted file mode 100644 index 5955dd9..0000000 --- a/nex-be/src/main/java/com/unisinsight/project/util/BtTorrentUtil.java +++ /dev/null @@ -1,429 +0,0 @@ -package com.unisinsight.project.util; - -import com.dampcake.bencode.BencodeInputStream; -import com.dampcake.bencode.BencodeOutputStream; - -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -/** - * BT种子生成和下载工具类 - */ -public class BtTorrentUtil { - - /** - * 创建BT种子文件 - * - * @param filePath 要分享的文件或目录路径 - * @param trackerUrl Tracker服务器地址 - * @param outputFile 生成的种子文件路径 - * @throws IOException IO异常 - * @throws URISyntaxException URI语法异常 - */ - public static void createTorrent(String filePath, String trackerUrl, String outputFile) - throws IOException, URISyntaxException { - - Path path = Paths.get(filePath); - if (!Files.exists(path)) { - throw new FileNotFoundException("文件或目录不存在: " + filePath); - } - - // 验证tracker URL格式 - new URI(trackerUrl); - - // 创建种子信息 - TorrentInfo torrentInfo = new TorrentInfo(); - torrentInfo.setAnnounce(trackerUrl); - torrentInfo.setCreationDate(new Date().getTime() / 1000); - torrentInfo.setCreatedBy("BtTorrentUtil v1.0"); - - // 处理文件信息 - if (Files.isDirectory(path)) { - processDirectory(path, torrentInfo); - } else { - processFile(path, torrentInfo); - } - - // 生成种子文件 - writeTorrentFile(torrentInfo, outputFile); - } - - /** - * 处理单个文件 - */ - private static void processFile(Path filePath, TorrentInfo torrentInfo) throws IOException { - FileInfo fileInfo = new FileInfo(); - fileInfo.setLength(Files.size(filePath)); - fileInfo.setPath(new ArrayList<>()); - fileInfo.getPath().add(filePath.getFileName().toString()); - - List files = new ArrayList<>(); - files.add(fileInfo); - - torrentInfo.setFiles(files); - torrentInfo.setName(filePath.getFileName().toString()); - torrentInfo.setPieceLength(524288); // 512KB - - // 计算文件的pieces - byte[] pieces = calculatePieces(filePath, torrentInfo.getPieceLength()); - torrentInfo.setPieces(pieces); - } - - /** - * 处理目录 - */ - private static void processDirectory(Path dirPath, TorrentInfo torrentInfo) throws IOException { - List files = new ArrayList<>(); - long totalSize = 0; - - Files.walk(dirPath) - .filter(Files::isRegularFile) - .forEach(file -> { - try { - FileInfo fileInfo = new FileInfo(); - fileInfo.setLength(Files.size(file)); - List pathList = new ArrayList<>(); - - // 获取相对路径 - Path relativePath = dirPath.relativize(file); - for (Path part : relativePath) { - pathList.add(part.toString()); - } - - fileInfo.setPath(pathList); - files.add(fileInfo); - } catch (IOException e) { - throw new RuntimeException(e); - } - }); - - torrentInfo.setFiles(files); - torrentInfo.setName(dirPath.getFileName().toString()); - torrentInfo.setPieceLength(524288); // 512KB - - // 计算所有文件的pieces(简化实现) - // 实际应用中需要按顺序读取所有文件数据来计算pieces - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for (FileInfo file : files) { - Path filePath = dirPath; - for (String pathPart : file.getPath()) { - filePath = filePath.resolve(pathPart); - } - if (Files.exists(filePath)) { - Files.copy(filePath, baos); - } - } - - byte[] pieces = calculatePiecesFromData(baos.toByteArray(), torrentInfo.getPieceLength()); - torrentInfo.setPieces(pieces); - } - - /** - * 计算文件的pieces哈希值 - */ - private static byte[] calculatePieces(Path filePath, int pieceLength) throws IOException { - ByteArrayOutputStream pieces = new ByteArrayOutputStream(); - byte[] buffer = new byte[pieceLength]; - int bytesRead; - - try (InputStream is = Files.newInputStream(filePath)) { - while ((bytesRead = is.read(buffer)) != -1) { - // 简化实现,实际应使用SHA1哈希 - // 这里只是示例,实际应用中需要计算SHA1 - byte[] hash = new byte[20]; // SHA1哈希长度为20字节 - for (int i = 0; i < Math.min(20, bytesRead); i++) { - hash[i] = (byte) (buffer[i] % 256); - } - pieces.write(hash); - } - } - - return pieces.toByteArray(); - } - - /** - * 从数据计算pieces哈希值 - */ - private static byte[] calculatePiecesFromData(byte[] data, int pieceLength) throws IOException { - ByteArrayOutputStream pieces = new ByteArrayOutputStream(); - - for (int i = 0; i < data.length; i += pieceLength) { - int length = Math.min(pieceLength, data.length - i); - byte[] piece = new byte[length]; - System.arraycopy(data, i, piece, 0, length); - - // 简化实现,实际应使用SHA1哈希 - byte[] hash = new byte[20]; - for (int j = 0; j < Math.min(20, length); j++) { - hash[j] = (byte) (piece[j] % 256); - } - pieces.write(hash); - } - - return pieces.toByteArray(); - } - - /** - * 写入种子文件 - */ - private static void writeTorrentFile(TorrentInfo torrentInfo, String outputFile) throws IOException { - try (FileOutputStream fos = new FileOutputStream(outputFile); - BencodeOutputStream bos = new BencodeOutputStream(fos)) { - - // 构建种子数据结构 - java.util.Map torrentMap = new java.util.HashMap<>(); - torrentMap.put("announce", torrentInfo.getAnnounce()); - torrentMap.put("creation date", torrentInfo.getCreationDate()); - torrentMap.put("created by", torrentInfo.getCreatedBy()); - - // info字典 - java.util.Map infoMap = new java.util.HashMap<>(); - infoMap.put("name", torrentInfo.getName()); - infoMap.put("piece length", torrentInfo.getPieceLength()); - infoMap.put("pieces", torrentInfo.getPieces()); - - if (torrentInfo.getFiles() != null && !torrentInfo.getFiles().isEmpty()) { - // 多文件模式 - List> fileList = new ArrayList<>(); - for (FileInfo file : torrentInfo.getFiles()) { - java.util.Map fileMap = new java.util.HashMap<>(); - fileMap.put("length", file.getLength()); - fileMap.put("path", file.getPath()); - fileList.add(fileMap); - } - infoMap.put("files", fileList); - } else { - // 单文件模式 - infoMap.put("length", torrentInfo.getLength()); - } - - torrentMap.put("info", infoMap); - - // 写入文件 - bos.writeDictionary(torrentMap); - } - } - - /** - * 提供种子文件下载 - * - * @param torrentFilePath 种子文件路径 - * @param downloadPath 下载保存路径 - * @throws IOException IO异常 - */ - public static void downloadTorrent(String torrentFilePath, String downloadPath) throws IOException { - Path source = Paths.get(torrentFilePath); - Path target = Paths.get(downloadPath); - - if (!Files.exists(source)) { - throw new FileNotFoundException("种子文件不存在: " + torrentFilePath); - } - - Files.copy(source, target); - } - - /** - * 解析种子文件信息 - * - * @param torrentFilePath 种子文件路径 - * @return 种子信息 - * @throws IOException IO异常 - */ - public static TorrentInfo parseTorrent(String torrentFilePath) throws IOException { - try (FileInputStream fis = new FileInputStream(torrentFilePath); - BencodeInputStream bis = new BencodeInputStream(fis)) { - - java.util.Map torrentMap = bis.readDictionary(); - TorrentInfo torrentInfo = new TorrentInfo(); - - torrentInfo.setAnnounce((String) torrentMap.get("announce")); - - Object creationDate = torrentMap.get("creation date"); - if (creationDate instanceof Number) { - torrentInfo.setCreationDate(((Number) creationDate).longValue()); - } - - torrentInfo.setCreatedBy((String) torrentMap.get("created by")); - - // 解析info部分 - java.util.Map infoMap = (java.util.Map) torrentMap.get("info"); - if (infoMap != null) { - torrentInfo.setName((String) infoMap.get("name")); - - Object pieceLength = infoMap.get("piece length"); - if (pieceLength instanceof Number) { - torrentInfo.setPieceLength(((Number) pieceLength).intValue()); - } - - torrentInfo.setPieces((byte[]) infoMap.get("pieces")); - - Object length = infoMap.get("length"); - if (length instanceof Number) { - torrentInfo.setLength(((Number) length).longValue()); - } - - // 处理文件列表 - Object filesObj = infoMap.get("files"); - if (filesObj instanceof List) { - List> fileList = - (List>) filesObj; - - List files = new ArrayList<>(); - for (java.util.Map fileMap : fileList) { - FileInfo fileInfo = new FileInfo(); - - Object fileLength = fileMap.get("length"); - if (fileLength instanceof Number) { - fileInfo.setLength(((Number) fileLength).longValue()); - } - - fileInfo.setPath((List) fileMap.get("path")); - files.add(fileInfo); - } - torrentInfo.setFiles(files); - } - } - - return torrentInfo; - } - } - - /** - * 种子信息类 - */ - public static class TorrentInfo { - private String announce; - private long creationDate; - private String createdBy; - private String name; - private long length; - private int pieceLength; - private byte[] pieces; - private List files; - - // Getters and Setters - public String getAnnounce() { - return announce; - } - - public void setAnnounce(String announce) { - this.announce = announce; - } - - public long getCreationDate() { - return creationDate; - } - - public void setCreationDate(long creationDate) { - this.creationDate = creationDate; - } - - public String getCreatedBy() { - return createdBy; - } - - public void setCreatedBy(String createdBy) { - this.createdBy = createdBy; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public long getLength() { - return length; - } - - public void setLength(long length) { - this.length = length; - } - - public int getPieceLength() { - return pieceLength; - } - - public void setPieceLength(int pieceLength) { - this.pieceLength = pieceLength; - } - - public byte[] getPieces() { - return pieces; - } - - public void setPieces(byte[] pieces) { - this.pieces = pieces; - } - - public List getFiles() { - return files; - } - - public void setFiles(List files) { - this.files = files; - } - } - - /** - * 文件信息类 - */ - public static class FileInfo { - private long length; - private List path; - - // Getters and Setters - public long getLength() { - return length; - } - - public void setLength(long length) { - this.length = length; - } - - public List getPath() { - return path; - } - - public void setPath(List path) { - this.path = path; - } - } - - public static void main(String[] args) { - try { - // 创建种子文件 - BtTorrentUtil.createTorrent( - "/path/to/your/file_or_directory", - "http://tracker.example.com:6969/announce", - "/path/to/output.torrent" - ); - - // 解析种子文件 - TorrentInfo info = BtTorrentUtil.parseTorrent("/path/to/output.torrent"); - System.out.println("种子名称: " + info.getName()); - System.out.println("创建时间: " + new Date(info.getCreationDate() * 1000)); - - // 下载种子文件 - BtTorrentUtil.downloadTorrent( - "/path/to/output.torrent", - "/path/to/download/location.torrent" - ); - - } catch (Exception e) { - e.printStackTrace(); - } - } -} - - - diff --git a/nex-be/src/main/java/com/unisinsight/project/util/BtTorrentUtils.java b/nex-be/src/main/java/com/unisinsight/project/util/BtTorrentUtils.java new file mode 100644 index 0000000..8786dba --- /dev/null +++ b/nex-be/src/main/java/com/unisinsight/project/util/BtTorrentUtils.java @@ -0,0 +1,97 @@ +package com.unisinsight.project.util; + +import javax.annotation.Resource; +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; + +/** + * 种子文件工具类 + */ +public class BtTorrentUtils { + + private static final String BT_SCRIPT_PATH = "/var/lib/vdi/nodejs/bttorrent.sh"; + + private static final Long WAIT_START_TIME = 8000L; + + /** + * 制作种子并开始做种 + * + * @param sourceFile 源文件路径(如 /data/file.iso) + * @param torrentFile 生成的种子文件路径(如 /data/file.torrent) + * @return 是否成功 + */ + public static boolean createAndSeed(String sourceFile, String torrentFile) { + // 验证文件是否存在 + if (!new File(sourceFile).exists()) { + System.err.println("源文件不存在: " + sourceFile); + return false; + } + // 确保目标目录存在 + new File(torrentFile).getParentFile().mkdirs(); + return executeCommand("start", sourceFile, torrentFile); + } + + + /** + * 停止指定文件的做种 + * + * @param sourceFile 源文件路径(如 /data/file.iso) + * @return 是否成功 + */ + public static boolean stopSeeding(String sourceFile) { + return executeCommand("stop_path", sourceFile, null); + } + + + + /** + * 执行 bttorrent.sh 脚本 + */ + private static boolean executeCommand(String command, String arg1, String arg2) { + try { + // 构造命令 + ProcessBuilder pb = new ProcessBuilder( + "bash", + BT_SCRIPT_PATH, + command, + arg1, + arg2 != null ? arg2 : "" // 处理可选参数 + ).redirectErrorStream(true); + // 启动进程 + Process process = pb.start(); + // 启动新线程读取输出,避免阻塞 + new Thread(() -> { + try { + logProcessOutput(process); + } catch (IOException e) { + e.printStackTrace(); + } + }).start(); + // 对于start命令,只要进程启动成功就返回true + if ("start".equals(command)) { + // 稍微等待一下看初始输出是否有错误 + Thread.sleep(WAIT_START_TIME); + return process.isAlive(); // 如果进程还在运行,认为启动成功 + } + // 对于stop命令,仍然等待完成 + return process.waitFor() == 0; + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + return false; + } + } + + /** + * 打印进程输出 + */ + private static void logProcessOutput(Process process) throws IOException { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + System.out.println("[BT输出] " + line); + } + } + } +}