From 34320634733b77a773af6836a2e3082092068ec9 Mon Sep 17 00:00:00 2001
From: chenhj <1263187585@qq.com>
Date: 星期六, 25 四月 2026 15:09:50 +0800
Subject: [PATCH] 增加永久文件接口

---
 src/main/java/com/ruoyi/basic/service/StorageBlobService.java           |    6 ++-
 src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java  |   38 ++++++++++++++++--
 src/main/java/com/ruoyi/basic/utils/FileUtil.java                       |   33 +++++++++++-----
 src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java |    5 ++
 src/main/java/com/ruoyi/project/common/CommonController.java            |   35 ++++++++++++++---
 5 files changed, 92 insertions(+), 25 deletions(-)

diff --git a/src/main/java/com/ruoyi/basic/service/StorageBlobService.java b/src/main/java/com/ruoyi/basic/service/StorageBlobService.java
index 1f09e2e..db9362e 100644
--- a/src/main/java/com/ruoyi/basic/service/StorageBlobService.java
+++ b/src/main/java/com/ruoyi/basic/service/StorageBlobService.java
@@ -23,9 +23,11 @@
      * @param files 鏂囦欢鍒楄〃
      * @return 涓婁紶缁撴灉
      */
-    List<StorageBlobVO> upload(List<MultipartFile> files);
+    List<StorageBlobVO> upload(List<MultipartFile> files, Boolean isPublic);
 
     File getFileByToken(String fileName, String token);
 
-    public String getDownloadFileName(String fileName);
+    File getPublicFile(String fileName, String publicKey);
+
+    String getDownloadFileName(String fileName);
 }
diff --git a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
index 9382134..ce0ba64 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/ProductModelServiceImpl.java
@@ -25,7 +25,10 @@
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.multipart.MultipartFile;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 /**
diff --git a/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java b/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
index 4fb85e2..9ad80ae 100644
--- a/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
+++ b/src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
@@ -20,6 +20,7 @@
 import javax.crypto.SecretKey;
 import java.io.File;
 import java.io.IOException;
+import java.math.BigDecimal;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.List;
@@ -39,7 +40,7 @@
     private final FileUtil fileUtil;
 
     @Override
-    public List<StorageBlobVO> upload(List<MultipartFile> files) {
+    public List<StorageBlobVO> upload(List<MultipartFile> files, Boolean isPublic) {
         if (CollectionUtils.isEmpty(files)) {
             throw new IllegalArgumentException("鏂囦欢涓嶈兘涓虹┖");
         }
@@ -65,7 +66,7 @@
             StorageBlobVO storageBlob;
             try {
                 file.transferTo(dest);
-                storageBlob = getStorageBlob(file, originalFileName, fileName, relativePath);
+                storageBlob = getStorageBlob(file, originalFileName, fileName, relativePath, isPublic);
                 if (storageBlob == null || storageBlob.getId() == null) {
                     throw new RuntimeException("鏂囦欢鍏冩暟鎹繚瀛樺け璐�");
                 }
@@ -109,13 +110,35 @@
         return new File(new File(properties.getPath(), path), fileName);
     }
 
+    @Override
+    public File getPublicFile(String fileName, String publicKey) {
+        if (!StringUtils.hasText(fileName)) {
+            throw new IllegalArgumentException("鏂囦欢鍚嶄笉鑳戒负绌�");
+        }
+        if (!StringUtils.hasText(publicKey)) {
+            throw new IllegalArgumentException("publicKey涓嶈兘涓虹┖");
+        }
+        StorageBlob storageBlob = storageBlobMapper.selectOne(new LambdaQueryWrapper<StorageBlob>()
+                .eq(StorageBlob::getUidFilename, fileName)
+                .eq(StorageBlob::getResourceKey, publicKey)
+                .last("limit 1"));
+        if (storageBlob == null) {
+            throw new IllegalArgumentException("鍏紑鏂囦欢涓嶅瓨鍦ㄦ垨publicKey涓嶅尮閰�");
+        }
+        String path = storageBlob.getPath();
+        if (!StringUtils.hasText(path)) {
+            return new File(properties.getPath(), fileName);
+        }
+        return new File(new File(properties.getPath(), path), fileName);
+    }
+
     private StorageBlob findStorageBlob(String fileName) {
         return storageBlobMapper.selectOne(new LambdaQueryWrapper<StorageBlob>()
                 .eq(StorageBlob::getUidFilename, fileName)
                 .last("limit 1"));
     }
 
-    private StorageBlobVO getStorageBlob(MultipartFile file, String originalFileName, String fileName, String relativePath) {
+    private StorageBlobVO getStorageBlob(MultipartFile file, String originalFileName, String fileName, String relativePath, Boolean isPublic) {
         StorageBlobVO storageBlob = new StorageBlobVO();
         storageBlob.setResourceKey(UUID.randomUUID().toString().replace("-", ""));
         storageBlob.setContentType(file.getContentType());
@@ -123,8 +146,13 @@
         storageBlob.setUidFilename(fileName);
         storageBlob.setByteSize(file.getSize());
         storageBlob.setPath(relativePath);
-        storageBlob.setPreviewURL(fileUtil.buildSignedPreviewUrl(storageBlob));
-        storageBlob.setDownloadURL(fileUtil.buildSignedDownloadUrl(storageBlob));
+        if (isPublic) {
+            storageBlob.setPreviewURL(fileUtil.buildSignedUrl(storageBlob, "/preview/", BigDecimal.valueOf(-1)));
+            storageBlob.setDownloadURL(fileUtil.buildSignedUrl(storageBlob, "/download/", BigDecimal.valueOf(-1)));
+        } else {
+            storageBlob.setPreviewURL(fileUtil.buildSignedPreviewUrl(storageBlob));
+            storageBlob.setDownloadURL(fileUtil.buildSignedDownloadUrl(storageBlob));
+        }
         int affectedRows = storageBlobMapper.insert(storageBlob);
         if (affectedRows <= 0) {
             throw new RuntimeException("鏂囦欢鍏冩暟鎹繚瀛樺け璐�");
diff --git a/src/main/java/com/ruoyi/basic/utils/FileUtil.java b/src/main/java/com/ruoyi/basic/utils/FileUtil.java
index 7af0cee..97397f3 100644
--- a/src/main/java/com/ruoyi/basic/utils/FileUtil.java
+++ b/src/main/java/com/ruoyi/basic/utils/FileUtil.java
@@ -565,8 +565,28 @@
         if (storageBlob == null || !StringUtils.hasText(storageBlob.getUidFilename())) {
             throw new IllegalArgumentException("鏂囦欢淇℃伅涓嶅畬鏁�");
         }
+        String domain = StringUtils.trimTrailingCharacter(properties.getDomain(), '/');
+        String prefix = properties.getUrlPrefix().startsWith("/") ? properties.getUrlPrefix() : "/" + properties.getUrlPrefix();
+        String normalizedActionPath = StringUtils.hasText(actionPath) ? actionPath : "/preview/";
+        if (!normalizedActionPath.startsWith("/")) {
+            normalizedActionPath = "/" + normalizedActionPath;
+        }
+        if (!normalizedActionPath.endsWith("/")) {
+            normalizedActionPath = normalizedActionPath + "/";
+        }
+        String baseUrl = domain + prefix + normalizedActionPath + storageBlob.getUidFilename();
+
+        // -1 琛ㄧず姘镐箙鏈夋晥锛屼笉鐢熸垚 token锛屾敼涓� publicKey 缁勫悎鏍¢獙
+        if (expired != null && BigDecimal.valueOf(-1L).compareTo(expired) == 0) {
+            if (!StringUtils.hasText(storageBlob.getResourceKey())) {
+                throw new IllegalArgumentException("鍏紑閾炬帴缂哄皯publicKey");
+            }
+            return baseUrl + "?publicKey=" + storageBlob.getResourceKey();
+        }
+
         long now = System.currentTimeMillis();
-        long expiredMillis = expired.multiply(new BigDecimal("60000")).longValue();
+        BigDecimal expiredValue = expired == null ? new BigDecimal("120") : expired;
+        long expiredMillis = expiredValue.multiply(new BigDecimal("60000")).longValue();
         if (expiredMillis <= 0L) {
             expiredMillis = 2L * 60L * 60L * 1000L;
         }
@@ -583,16 +603,7 @@
                 .signWith(key)            // 閲嶇偣锛氫紶鍏ヤ笂闈㈢敓鎴愮殑 key 瀵硅薄锛岃�屼笉鏄� String
                 .compact();
         cacheTokenUsage(token, expiredMillis);
-        String domain = StringUtils.trimTrailingCharacter(properties.getDomain(), '/');
-        String prefix = properties.getUrlPrefix().startsWith("/") ? properties.getUrlPrefix() : "/" + properties.getUrlPrefix();
-        String normalizedActionPath = StringUtils.hasText(actionPath) ? actionPath : "/preview/";
-        if (!normalizedActionPath.startsWith("/")) {
-            normalizedActionPath = "/" + normalizedActionPath;
-        }
-        if (!normalizedActionPath.endsWith("/")) {
-            normalizedActionPath = normalizedActionPath + "/";
-        }
-        return domain + prefix + normalizedActionPath + storageBlob.getUidFilename() + "?token=" + token;
+        return baseUrl + "?token=" + token;
     }
 
     private void cacheTokenUsage(String token, long expiredMillis) {
diff --git a/src/main/java/com/ruoyi/project/common/CommonController.java b/src/main/java/com/ruoyi/project/common/CommonController.java
index bf7bda4..9ee3c93 100644
--- a/src/main/java/com/ruoyi/project/common/CommonController.java
+++ b/src/main/java/com/ruoyi/project/common/CommonController.java
@@ -128,15 +128,32 @@
 //    }
     @PostMapping({"/upload"})
     @Operation(summary = "鏂囦欢涓婁紶")
-    public R upload(@RequestParam("files") List<MultipartFile> files) throws Exception {
-        return R.ok(storageBlobService.upload(files));
+    public R upload(@RequestParam("files") List<MultipartFile> files) {
+        return R.ok(storageBlobService.upload(files, false));
     }
 
+    /**
+     * 鍏叡鏂囦欢涓婁紶
+     * 姝ゆ帴鍙d笂浼犵殑鏂囦欢姘镐箙鏈夋晥锛屾厧鐢�
+     */
+    @PostMapping({"/public/upload"})
+    @Operation(summary = "鏂囦欢涓婁紶")
+    public R publicUpload(@RequestParam("files") List<MultipartFile> files) {
+        return R.ok(storageBlobService.upload(files, true));
+    }
 
     @GetMapping("/download/{fileName}")
     @Anonymous
-    public void download(@PathVariable String fileName, @RequestParam("token") String token, HttpServletResponse response) throws Exception {
-        File file = storageBlobService.getFileByToken(fileName, token);
+    public void download(@PathVariable String fileName,
+                         @RequestParam(value = "token", required = false) String token,
+                         @RequestParam(value = "publicKey", required = false) String publicKey,
+                         HttpServletResponse response) throws Exception {
+        File file;
+        if (publicKey != null) {
+            file = fileUtil.compressFile(storageBlobService.getPublicFile(fileName, publicKey));
+        } else {
+            file = fileUtil.compressFile(storageBlobService.getFileByToken(fileName, token));
+        }
         String originalFileName = storageBlobService.getDownloadFileName(fileName);
         String encodedFileName = URLEncoder.encode(originalFileName, StandardCharsets.UTF_8.name()).replace("+", "%20");
         response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + encodedFileName);
@@ -147,8 +164,14 @@
     @GetMapping("/preview/{fileName}")
     @Anonymous
     public ResponseEntity<FileSystemResource> preview(@PathVariable String fileName,
-                                                      @RequestParam("token") String token) throws Exception {
-        File file = fileUtil.compressFile(storageBlobService.getFileByToken(fileName, token));
+                                                      @RequestParam(value = "token", required = false) String token,
+                                                      @RequestParam(value = "publicKey", required = false) String publicKey) throws Exception {
+        File file;
+        if (publicKey != null) {
+            file = fileUtil.compressFile(storageBlobService.getPublicFile(fileName, publicKey));
+        } else {
+            file = fileUtil.compressFile(storageBlobService.getFileByToken(fileName, token));
+        }
         String contentType = Files.probeContentType(file.toPath());
 
         ContentDisposition contentDisposition = ContentDisposition.inline()

--
Gitblit v1.9.3