feat(file):文件管理接口

Change-Id: I0ad3e90c49c47e2bf31a8ac2c3d14463cb65de38
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/torrent/util/TorrentFileUtil.java b/ruoyi-admin/src/main/java/com/ruoyi/torrent/util/TorrentFileUtil.java
new file mode 100644
index 0000000..a1cd422
--- /dev/null
+++ b/ruoyi-admin/src/main/java/com/ruoyi/torrent/util/TorrentFileUtil.java
@@ -0,0 +1,234 @@
+package com.ruoyi.torrent.util;
+
+import org.springframework.web.multipart.MultipartFile;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.*;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.util.*;
+import java.util.stream.Collectors;
+
+public class TorrentFileUtil {
+
+    /**
+     * 文件上传工具方法
+     *
+     * @param file     上传的文件对象
+     * @param savePath 文件保存路径
+     * @return 返回包含文件信息的Map
+     * @throws IOException 文件操作异常
+     */
+    public static Map<String, Object> uploadFile(MultipartFile file, String savePath) throws IOException {
+        if (file.isEmpty()) {
+            throw new IllegalArgumentException("上传文件不能为空");
+        }
+
+        // 确保目录存在
+        Path saveDir = Paths.get(savePath);
+        if (!Files.exists(saveDir)) {
+            Files.createDirectories(saveDir);
+        }
+
+        // 获取文件信息
+        String originalFilename = Objects.requireNonNull(file.getOriginalFilename());
+        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
+        String storedFilename = System.currentTimeMillis() + "_" + UUID.randomUUID() + fileExtension;
+        long fileSize = file.getSize();
+
+        // 保存文件
+        Path targetPath = saveDir.resolve(storedFilename);
+        Files.copy(file.getInputStream(), targetPath, StandardCopyOption.REPLACE_EXISTING);
+
+        // 返回文件信息
+        Map<String, Object> fileInfo = new HashMap<>();
+        fileInfo.put("originalName", originalFilename);
+        fileInfo.put("storedName", storedFilename);
+        fileInfo.put("filePath", targetPath.toString());
+        fileInfo.put("fileSize", fileSize);
+        fileInfo.put("fileType", fileExtension.substring(1));
+        fileInfo.put("uploadTime", new Date());
+
+        return fileInfo;
+    }
+
+    /**
+     * 文件下载工具方法
+     *
+     * @param response   HttpServletResponse对象
+     * @param filePath   要下载的文件路径
+     * @param fileName   下载时显示的文件名
+     * @param deleteAfterDownload 下载后是否删除原文件
+     * @throws IOException 文件操作异常
+     */
+    public static void downloadFile(HttpServletResponse response, String filePath,
+                                    String fileName, boolean deleteAfterDownload) throws IOException {
+        Path file = Paths.get(filePath);
+        if (!Files.exists(file)) {
+            throw new FileNotFoundException("文件不存在: " + filePath);
+        }
+
+        // 设置响应头
+        response.setContentType("application/octet-stream");
+        response.setHeader("Content-Disposition",
+                "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
+        response.setContentLength((int) Files.size(file));
+
+        // 使用NIO提高性能
+        try (InputStream is = Files.newInputStream(file);
+             OutputStream os = response.getOutputStream()) {
+            byte[] buffer = new byte[4096];
+            int bytesRead;
+            while ((bytesRead = is.read(buffer)) != -1) {
+                os.write(buffer, 0, bytesRead);
+            }
+            os.flush();
+        }
+
+        // 下载后删除原文件
+        if (deleteAfterDownload) {
+            Files.delete(file);
+        }
+    }
+
+    /**
+     * 文件下载工具方法(简化版,不删除原文件)
+     */
+    public static void downloadFile(HttpServletResponse response, String filePath, String fileName) throws IOException {
+        downloadFile(response, filePath, fileName, false);
+    }
+
+    /**
+     * 删除文件
+     *
+     * @param filePath 文件路径
+     * @return 是否删除成功
+     */
+    public static boolean deleteFile(String filePath) {
+        try {
+            return Files.deleteIfExists(Paths.get(filePath));
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 重命名文件
+     *
+     * @param oldPath 原文件路径
+     * @param newName 新文件名(不含路径)
+     * @return 新文件路径
+     * @throws IOException 文件操作异常
+     */
+    public static String renameFile(String oldPath, String newName) throws IOException {
+        Path source = Paths.get(oldPath);
+        Path target = source.resolveSibling(newName);
+        return Files.move(source, target, StandardCopyOption.REPLACE_EXISTING).toString();
+    }
+
+    /**
+     * 获取文件信息
+     *
+     * @param filePath 文件路径
+     * @return 包含文件信息的Map
+     * @throws IOException 文件操作异常
+     */
+    public static Map<String, Object> getFileInfo(String filePath) throws IOException {
+        Path path = Paths.get(filePath);
+        if (!Files.exists(path)) {
+            return null;
+        }
+
+        Map<String, Object> fileInfo = new HashMap<>();
+        fileInfo.put("fileName", path.getFileName().toString());
+        fileInfo.put("filePath", path.toString());
+        fileInfo.put("fileSize", Files.size(path));
+        fileInfo.put("lastModified", new Date(Files.getLastModifiedTime(path).toMillis()));
+        fileInfo.put("isDirectory", Files.isDirectory(path));
+
+        return fileInfo;
+    }
+
+    /**
+     * 获取目录下的文件列表
+     *
+     * @param dirPath 目录路径
+     * @return 文件信息列表
+     * @throws IOException 文件操作异常
+     */
+    public static List<Map<String, Object>> listFiles(String dirPath) throws IOException {
+        return Files.list(Paths.get(dirPath))
+                .map(path -> {
+                    try {
+                        Map<String, Object> fileInfo = new HashMap<>();
+                        fileInfo.put("name", path.getFileName().toString());
+                        fileInfo.put("path", path.toString());
+                        fileInfo.put("size", Files.size(path));
+                        fileInfo.put("lastModified", new Date(Files.getLastModifiedTime(path).toMillis()));
+                        fileInfo.put("isDirectory", Files.isDirectory(path));
+                        return fileInfo;
+                    } catch (IOException e) {
+                        return null;
+                    }
+                })
+                .filter(Objects::nonNull)
+                .collect(Collectors.toList());
+    }
+
+    /**
+     * 复制文件
+     *
+     * @param sourcePath 源文件路径
+     * @param targetPath 目标文件路径
+     * @return 是否复制成功
+     */
+    public static boolean copyFile(String sourcePath, String targetPath) {
+        try {
+            Files.copy(Paths.get(sourcePath), Paths.get(targetPath), StandardCopyOption.REPLACE_EXISTING);
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    /**
+     * 获取文件大小(格式化字符串)
+     *
+     * @param size 文件大小(字节)
+     * @return 格式化后的字符串
+     */
+    public static String formatFileSize(long size) {
+        if (size <= 0) return "0B";
+        String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
+        int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
+        return String.format("%.2f %s", size / Math.pow(1024, digitGroups), units[digitGroups]);
+    }
+
+    /**
+     * 检查文件是否存在
+     *
+     * @param filePath 文件路径
+     * @return 是否存在
+     */
+    public static boolean fileExists(String filePath) {
+        return Files.exists(Paths.get(filePath));
+    }
+
+    /**
+     * 创建目录
+     *
+     * @param dirPath 目录路径
+     * @return 是否创建成功
+     */
+    public static boolean createDirectory(String dirPath) {
+        try {
+            Files.createDirectories(Paths.get(dirPath));
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+}
\ No newline at end of file