From 1ca5584d7e3200a9af65a099bd26d3593e2ba702 Mon Sep 17 00:00:00 2001
From: liyong <18434998025@163.com>
Date: 星期四, 07 五月 2026 14:36:08 +0800
Subject: [PATCH] 迁移pro

---
 src/main/java/com/ruoyi/basic/utils/FileUtil.java |  847 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 847 insertions(+), 0 deletions(-)

diff --git a/src/main/java/com/ruoyi/basic/utils/FileUtil.java b/src/main/java/com/ruoyi/basic/utils/FileUtil.java
new file mode 100644
index 0000000..c57468a
--- /dev/null
+++ b/src/main/java/com/ruoyi/basic/utils/FileUtil.java
@@ -0,0 +1,847 @@
+package com.ruoyi.basic.utils;
+
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.ruoyi.basic.dto.StorageAttachmentDTO;
+import com.ruoyi.basic.dto.StorageAttachmentVO;
+import com.ruoyi.basic.dto.StorageBlobDTO;
+import com.ruoyi.basic.dto.StorageBlobVO;
+import com.ruoyi.basic.enums.ApplicationTypeEnum;
+import com.ruoyi.basic.enums.RecordTypeEnum;
+import com.ruoyi.basic.mapper.StorageAttachmentMapper;
+import com.ruoyi.basic.mapper.StorageBlobMapper;
+import com.ruoyi.basic.pojo.StorageAttachment;
+import com.ruoyi.basic.pojo.StorageBlob;
+import com.ruoyi.common.config.FileProperties;
+import io.jsonwebtoken.Jwts;
+import io.jsonwebtoken.security.Keys;
+import lombok.RequiredArgsConstructor;
+import net.coobird.thumbnailator.Thumbnails;
+import org.springframework.beans.BeanUtils;
+import org.springframework.data.redis.core.StringRedisTemplate;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.crypto.SecretKey;
+import java.io.File;
+import java.math.BigDecimal;
+import java.nio.charset.StandardCharsets;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
+@Component
+@RequiredArgsConstructor
+public class FileUtil {
+    private final FileProperties properties;
+    private final StorageAttachmentMapper storageAttachmentMapper;
+    private final StringRedisTemplate stringRedisTemplate;
+    private final StorageBlobMapper storageBlobMapper;
+
+    private static final String TOKEN_USAGE_KEY_PREFIX = "file:token:usage:";
+    private static final DateTimeFormatter YEAR_PATH_FORMATTER = DateTimeFormatter.ofPattern("yyyy");
+    private static final DateTimeFormatter MONTH_DAY_PATH_FORMATTER = DateTimeFormatter.ofPattern("MMdd");
+
+    /**
+     * 淇濆瓨闄勪欢淇℃伅
+     *
+     * @param application     鏂囦欢鐢ㄩ��
+     * @param recordType      鍏宠仈璁板綍绫诲瀷
+     * @param recordId        鍏宠仈璁板綍id
+     * @param storageBlobDTOS 鏂囦欢淇℃伅
+     */
+    public void saveStorageAttachment(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS) {
+        if (application == null) {
+            throw new RuntimeException("鏂囦欢鐢ㄩ�斾笉鑳戒负绌�");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        // 鍒犻櫎鏃ч檮浠朵俊鎭�
+        deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageBlobDTOS)) {
+            return;
+        }
+        List<StorageAttachment> storageAttachments = new ArrayList<>();
+        for (StorageBlobDTO storageBlobDTO : storageBlobDTOS) {
+            StorageAttachment storageAttachment = new StorageAttachment();
+            storageAttachment.setApplication(application.getType());
+            storageAttachment.setRecordType(recordType.getType());
+            storageAttachment.setRecordId(recordId);
+            storageAttachment.setStorageBlobId(storageBlobDTO.getId());
+            storageAttachment.setDeleted(0L);
+            storageAttachments.add(storageAttachment);
+        }
+        storageAttachmentMapper.insert(storageAttachments);
+    }
+
+    /**
+     * 淇濆瓨闄勪欢淇℃伅
+     *
+     * @param recordType      鍏宠仈璁板綍绫诲瀷
+     * @param recordId        鍏宠仈璁板綍id
+     * @param storageBlobDTOS 鏂囦欢淇℃伅
+     */
+    public void saveStorageAttachmentByRecordTypeAndRecordId(String application,RecordTypeEnum recordType, Long recordId, List<StorageBlobDTO> storageBlobDTOS) {
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        if (recordId == null) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+
+        // 鍒犻櫎鏃ч檮浠朵俊鎭�
+        if (application == null) {
+            for (StorageBlobDTO storageBlobDTO : storageBlobDTOS) {
+                deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.getByType(storageBlobDTO.getApplication()), recordType, recordId);
+            }
+        } else {
+            deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum.getByType(application), recordType, recordId);
+        }
+
+        if (CollectionUtils.isEmpty(storageBlobDTOS)) {
+            deleteStorageAttachmentsByRecordTypeAndRecordId(recordType, recordId);
+        }
+
+        List<StorageAttachment> storageAttachments = new ArrayList<>();
+        for (StorageBlobDTO storageBlobDTO : storageBlobDTOS) {
+            StorageAttachment storageAttachment = new StorageAttachment();
+            storageAttachment.setApplication(Objects.requireNonNullElseGet(application, () -> ApplicationTypeEnum.getByType(storageBlobDTO.getApplication()).getType()));
+            storageAttachment.setRecordType(recordType.getType());
+            storageAttachment.setRecordId(recordId);
+            storageAttachment.setStorageBlobId(storageBlobDTO.getId());
+            storageAttachment.setDeleted(0L);
+            storageAttachments.add(storageAttachment);
+        }
+        storageAttachmentMapper.insert(storageAttachments);
+    }
+
+    /**
+     * 鍒犻櫎鏂囦欢淇℃伅
+     *
+     * @param storageBlobIds 鏂囦欢id
+     */
+    public void deleteStorageBlobs(List<Long> storageBlobIds) {
+        storageBlobMapper.deleteByIds(storageBlobIds);
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鍒犻櫎鏂囦欢淇℃伅
+     *
+     * @param storageAttachmentIds 鏂囦欢id
+     */
+    public void deleteStorageBlobsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectByIds(storageAttachmentIds);
+        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
+        deleteStorageBlobs(storageBlobIds);
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鍒犻櫎鏂囦欢淇℃伅
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordIds    鍏宠仈璁板綍id
+     */
+    public void deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds) {
+        if (recordIds == null || recordIds.isEmpty()) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (application == null) {
+            throw new RuntimeException("鏂囦欢鐢ㄩ�斾笉鑳戒负绌�");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .in(StorageAttachment::getRecordId, recordIds)
+                .eq(StorageAttachment::getApplication, application.getType()));
+        if (CollectionUtils.isNotEmpty(storageAttachments)) {
+            List<Long> storageAttachmentIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId)
+                    .collect(Collectors.toList());
+            deleteStorageBlobsByStorageAttachmentIds(storageAttachmentIds);
+        }
+    }
+
+    /**
+     * 閫氳繃鍏宠仈璁板綍绫诲瀷銆佸叧鑱旇褰昳d鍒犻櫎鏂囦欢淇℃伅
+     *
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public void deleteStorageBlobsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId));
+        if (CollectionUtils.isNotEmpty(storageAttachments)) {
+            List<Long> storageAttachmentIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId)
+                    .collect(Collectors.toList());
+            deleteStorageBlobsByStorageAttachmentIds(storageAttachmentIds);
+        }
+    }
+
+    /**
+     * 鍒犻櫎鏂囦欢鍏宠仈淇℃伅
+     *
+     * @param storageAttachmentIds 鏂囦欢鍏宠仈id
+     */
+    public void deleteStorageAttachmentsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+        deleteStorageBlobsByStorageAttachmentIds(storageAttachmentIds);
+        storageAttachmentMapper.deleteByIds(storageAttachmentIds);
+    }
+
+    /**
+     * 鍒犻櫎鏂囦欢鍏宠仈淇℃伅
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public void deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (application == null) {
+            throw new RuntimeException("鏂囦欢鐢ㄩ�斾笉鑳戒负绌�");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(application, recordType, Arrays.asList(recordId));
+        storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId)
+                .eq(StorageAttachment::getApplication, application.getType()));
+    }
+
+    /**
+     * 鍒犻櫎鏂囦欢鍏宠仈淇℃伅
+     *
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public void deleteStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        deleteStorageBlobsByRecordTypeAndRecordId(recordType, recordId);
+        storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId));
+    }
+
+    /**
+     * 鎵归噺鍒犻櫎鏂囦欢鍏宠仈淇℃伅 attachment
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordIds   鍏宠仈璁板綍id
+     */
+    public void deleteStorageAttachmentsByApplicationAndRecordTypeAndRecordIds(ApplicationTypeEnum application, RecordTypeEnum recordType, List<Long> recordIds) {
+        if (recordIds == null || recordIds.isEmpty()) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (application == null) {
+            throw new RuntimeException("鏂囦欢鐢ㄩ�斾笉鑳戒负绌�");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        deleteStorageBlobsByApplicationAndRecordTypeAndRecordIds(application, recordType, recordIds);
+        storageAttachmentMapper.delete(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .in(StorageAttachment::getRecordId, recordIds)
+                .eq(StorageAttachment::getApplication, application.getType()));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢淇℃伅 attachment
+     *
+     * @param storageAttachmentIds 鏂囦欢id
+     */
+    public List<StorageAttachment> getStorageAttachmentsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+        if (CollectionUtils.isEmpty(storageAttachmentIds)) {
+            throw new RuntimeException("鏂囦欢id涓嶈兘涓虹┖");
+        }
+        return storageAttachmentMapper.selectByIds(storageAttachmentIds);
+    }
+
+    /**
+     * 閫氳繃璁板綍绫诲瀷鑾峰彇鏂囦欢淇℃伅 attachment锛堝垎椤碉級
+     *
+     * @param storageAttachmentDTO 鍏宠仈璁板綍淇℃伅
+     */
+    public List<StorageBlobVO> getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(StorageAttachmentDTO storageAttachmentDTO) {
+        LambdaQueryWrapper<StorageAttachment> queryWrapper = new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, storageAttachmentDTO.getRecordType())
+                .eq(StorageAttachment::getRecordId, storageAttachmentDTO.getRecordId());
+        if (storageAttachmentDTO.getApplication() != null) {
+            queryWrapper.eq(StorageAttachment::getApplication, storageAttachmentDTO.getApplication());
+        }
+        List<StorageAttachment> storageAttachments = storageAttachmentMapper.selectList(queryWrapper);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return null;
+        }
+        return getStorageBlobVOsByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢鍏宠仈淇℃伅 attachment
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public List<StorageAttachment> getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }        if (application == null) {
+            throw new RuntimeException("鏂囦欢鐢ㄩ�斾笉鑳戒负绌�");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        return storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId)
+                .eq(StorageAttachment::getApplication, application.getType()));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢淇℃伅 blob
+     *
+     * @param storageAttachmentIds 鏂囦欢id
+     */
+    public List<StorageBlobVO> getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return null;
+        }
+        Map<Long, Long> blobIdToAttachmentIdMap = storageAttachments.stream()
+                .collect(Collectors.toMap(StorageAttachment::getStorageBlobId, StorageAttachment::getId));
+
+        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
+        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
+        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
+        for (StorageBlob storageBlob : storageBlobs) {
+            StorageBlobVO storageBlobVO = new StorageBlobVO();
+            BeanUtils.copyProperties(storageBlob, storageBlobVO);
+            storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO));
+            storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO));
+            storageBlobVO.setStorageAttachmentId(blobIdToAttachmentIdMap.get(storageBlob.getId()));
+            storageBlobDTOS.add(storageBlobVO);
+        }
+        return storageBlobDTOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢鍏宠仈淇℃伅 attachment
+     *
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public List<StorageAttachment> getStorageAttachmentsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        if (recordId == null || recordId <= 0) {
+            throw new RuntimeException("鍏宠仈璁板綍id涓嶈兘涓虹┖");
+        }
+        if (recordType == null) {
+            throw new RuntimeException("鍏宠仈璁板綍绫诲瀷涓嶈兘涓虹┖");
+        }
+        return storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
+                .eq(StorageAttachment::getRecordType, recordType.getType())
+                .eq(StorageAttachment::getRecordId, recordId));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢淇℃伅 blob
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public List<StorageBlobVO> getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return null;
+        }
+        // 鏋勫缓 storageBlobId -> storageAttachmentId 鐨勬槧灏�
+        Map<Long, Long> blobIdToAttachmentIdMap = storageAttachments.stream()
+                .collect(Collectors.toMap(StorageAttachment::getStorageBlobId, StorageAttachment::getId));
+
+        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
+        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
+        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
+        for (StorageBlob storageBlob : storageBlobs) {
+            StorageBlobVO storageBlobVO = new StorageBlobVO();
+            BeanUtils.copyProperties(storageBlob, storageBlobVO);
+            storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO));
+            storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO));
+            storageBlobVO.setStorageAttachmentId(blobIdToAttachmentIdMap.get(storageBlob.getId()));
+            storageBlobDTOS.add(storageBlobVO);
+        }
+        return storageBlobDTOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢淇℃伅 blob
+     *
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     */
+    public List<StorageBlobVO> getStorageBlobVOsByRecordTypeAndRecordId(RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByRecordTypeAndRecordId(recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return null;
+        }
+        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
+        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
+        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
+        for (StorageBlob storageBlob : storageBlobs) {
+            StorageBlobVO storageBlobVO = new StorageBlobVO();
+            BeanUtils.copyProperties(storageBlob, storageBlobVO);
+            storageBlobVO.setPreviewURL(buildSignedPreviewUrl(storageBlobVO));
+            storageBlobVO.setDownloadURL(buildSignedDownloadUrl(storageBlobVO));
+            storageBlobDTOS.add(storageBlobVO);
+        }
+        return storageBlobDTOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鐢ㄩ�斻�佸叧鑱旇褰曠被鍨嬨�佸叧鑱旇褰昳d鑾峰彇鏂囦欢淇℃伅 鑷畾涔夎繃鏈熸椂闂达紙鍒嗛挓锛� blob
+     *
+     * @param application 鏂囦欢鐢ㄩ��
+     * @param recordType  鍏宠仈璁板綍绫诲瀷
+     * @param recordId    鍏宠仈璁板綍id
+     * @param expired     杩囨湡鏃堕棿
+     */
+    public List<StorageBlobVO> getStorageBlobVOsByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return null;
+        }
+        // 鏋勫缓 storageBlobId -> storageAttachmentId 鐨勬槧灏�
+        Map<Long, Long> blobIdToAttachmentIdMap = storageAttachments.stream()
+                .collect(Collectors.toMap(StorageAttachment::getStorageBlobId, StorageAttachment::getId));
+
+        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
+        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
+        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
+        for (StorageBlob storageBlob : storageBlobs) {
+            StorageBlobVO storageBlobVO = new StorageBlobVO();
+            BeanUtils.copyProperties(storageBlob, storageBlobVO);
+            storageBlobVO.setPreviewURL(buildSignedUrl(storageBlobVO, "/preview/", expired));
+            storageBlobVO.setDownloadURL(buildSignedUrl(storageBlobVO, "/download/", expired));
+            storageBlobVO.setStorageAttachmentId(blobIdToAttachmentIdMap.get(storageBlob.getId()));
+            storageBlobDTOS.add(storageBlobVO);
+        }
+        return storageBlobDTOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢淇℃伅瀛樺湪杩囨湡鏃堕棿 鑷畾涔夎繃鏈熸椂闂达紙鍒嗛挓锛� blob
+     *
+     * @param storageAttachmentIds 鏂囦欢id
+     * @param expired              杩囨湡鏃堕棿
+     */
+    public List<StorageBlobVO> getStorageBlobVOsByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return null;
+        }
+        List<Long> storageBlobIds = storageAttachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
+        List<StorageBlob> storageBlobs = storageBlobMapper.selectByIds(storageBlobIds);
+        List<StorageBlobVO> storageBlobDTOS = new ArrayList<>();
+        for (StorageBlob storageBlob : storageBlobs) {
+            StorageBlobVO storageBlobVO = new StorageBlobVO();
+            BeanUtils.copyProperties(storageBlob, storageBlobVO);
+            storageBlobVO.setPreviewURL(buildSignedUrl(storageBlobVO, "/preview/", expired));
+            storageBlobVO.setDownloadURL(buildSignedUrl(storageBlobVO, "/download/", expired));
+            storageBlobDTOS.add(storageBlobVO);
+        }
+        return storageBlobDTOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢淇℃伅 attachment
+     *
+     * @param storageAttachmentIds 鏂囦欢id
+     */
+    public List<StorageAttachmentVO> getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
+        for (StorageAttachment storageAttachment : storageAttachments) {
+            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
+            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
+            if (CollectionUtils.isEmpty(storageBlobVOS)) {
+                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
+            } else {
+                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
+            }
+            storageAttachmentVOS.add(storageAttachmentVO);
+        }
+        return storageAttachmentVOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢淇℃伅瀛樺湪杩囨湡鏃堕棿 鑷畾涔夎繃鏈熸椂闂达紙鍒嗛挓锛� attachment
+     *
+     * @param storageAttachmentIds 鏂囦欢id
+     * @param expired              杩囨湡鏃堕棿
+     */
+    public List<StorageAttachmentVO> getStorageAttachmentVOSByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByStorageAttachmentIds(storageAttachmentIds);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
+        for (StorageAttachment storageAttachment : storageAttachments) {
+            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
+            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
+            if (CollectionUtils.isEmpty(storageBlobVOS)) {
+                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
+            } else {
+                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
+            }
+            storageAttachmentVOS.add(storageAttachmentVO);
+        }
+        return storageAttachmentVOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢淇℃伅 attachment
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     */
+    public List<StorageAttachmentVO> getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
+        for (StorageAttachment storageAttachment : storageAttachments) {
+            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
+            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()));
+            if (CollectionUtils.isEmpty(storageBlobVOS)) {
+                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
+            } else {
+                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
+            }
+            storageAttachmentVOS.add(storageAttachmentVO);
+        }
+        return storageAttachmentVOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢淇℃伅瀛樺湪杩囨湡鏃堕棿 鑷畾涔夎繃鏈熸椂闂达紙鍒嗛挓锛� attachment
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     * @param expired     杩囨湡鏃堕棿
+     */
+    public List<StorageAttachmentVO> getStorageAttachmentVOSByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        List<StorageAttachmentVO> storageAttachmentVOS = new ArrayList<>();
+        for (StorageAttachment storageAttachment : storageAttachments) {
+            StorageAttachmentVO storageAttachmentVO = new StorageAttachmentVO();
+            BeanUtils.copyProperties(storageAttachment, storageAttachmentVO);
+            List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(Collections.singletonList(storageAttachment.getId()), expired);
+            if (CollectionUtils.isEmpty(storageBlobVOS)) {
+                storageAttachmentVO.setStorageBlobVOS(new ArrayList<>());
+            } else {
+                storageAttachmentVO.setStorageBlobVOS(storageBlobVOS);
+            }
+            storageAttachmentVOS.add(storageAttachmentVO);
+        }
+        return storageAttachmentVOS;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢棰勮鍦板潃
+     *
+     * @param storageAttachmentIds 鏂囦欢鍏宠仈id
+     */
+    public List<String> getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+        List<String> res = new ArrayList<>();
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
+        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
+            res.add(buildSignedPreviewUrl(storageBlobVO));
+        }
+        return res;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢棰勮鍦板潃瀛樺湪杩囨湡鏃堕棿 鑷畾涔夎繃鏈熸椂闂达紙鍒嗛挓锛�
+     *
+     * @param storageAttachmentIds 鏂囦欢鍏宠仈id
+     * @param expired              杩囨湡鏃堕棿
+     */
+    public List<String> getFilePreviewURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
+        List<String> res = new ArrayList<>();
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
+        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
+            res.add(buildSignedUrl(storageBlobVO, "/preview/", expired));
+        }
+        return res;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢涓嬭浇鍦板潃
+     *
+     * @param storageAttachmentIds 鏂囦欢鍏宠仈id
+     */
+    public List<String> getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds) {
+        List<String> res = new ArrayList<>();
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
+        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
+            res.add(buildSignedDownloadUrl(storageBlobVO));
+        }
+        return res;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢涓嬭浇鍦板潃瀛樺湪杩囨湡鏃堕棿 鑷畾涔夎繃鏈熸椂闂达紙鍒嗛挓锛�
+     *
+     * @param storageAttachmentIds 鏂囦欢鍏宠仈id
+     * @param expired              杩囨湡鏃堕棿
+     */
+    public List<String> getFileDownloadURLByStorageAttachmentIds(List<Long> storageAttachmentIds, BigDecimal expired) {
+        List<String> res = new ArrayList<>();
+        List<StorageBlobVO> storageBlobVOS = getStorageBlobVOsByStorageAttachmentIds(storageAttachmentIds);
+        for (StorageBlobVO storageBlobVO : storageBlobVOS) {
+            res.add(buildSignedUrl(storageBlobVO, "/download/", expired));
+        }
+        return res;
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢棰勮鍦板潃
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     */
+    public List<String> getFilePreviewURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFilePreviewURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢棰勮鍦板潃瀛樺湪杩囨湡鏃堕棿
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     * @param expired     杩囨湡鏃堕棿锛堝垎閽燂級
+     */
+    public List<String> getFilePreviewURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFilePreviewURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()), expired);
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢涓嬭浇鍦板潃
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     */
+    public List<String> getFileDownloadURLByApplicationAndRecordTypeAndRecordId(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFileDownloadURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()));
+    }
+
+    /**
+     * 閫氳繃鏂囦欢鍏宠仈id鑾峰彇鏂囦欢涓嬭浇鍦板潃瀛樺湪杩囨湡鏃堕棿
+     *
+     * @param application 搴旂敤
+     * @param recordType  璁板綍绫诲瀷
+     * @param recordId    璁板綍id
+     * @param expired     杩囨湡鏃堕棿锛堝垎閽燂級
+     */
+    public List<String> getFileDownloadURLByApplicationAndRecordTypeAndRecordIdAndExpired(ApplicationTypeEnum application, RecordTypeEnum recordType, Long recordId, BigDecimal expired) {
+        List<StorageAttachment> storageAttachments = getStorageAttachmentsByApplicationAndRecordTypeAndRecordId(application, recordType, recordId);
+        if (CollectionUtils.isEmpty(storageAttachments)) {
+            return new ArrayList<>();
+        }
+        return getFileDownloadURLByStorageAttachmentIds(storageAttachments.stream().map(StorageAttachment::getId).collect(Collectors.toList()), expired);
+    }
+
+    public String buildSignedPreviewUrl(StorageBlobVO storageBlob) {
+        return buildSignedUrl(storageBlob, "/preview/", properties.getExpired());
+    }
+    public String buildSignedDownloadUrl(StorageBlobVO storageBlob) {
+        return buildSignedUrl(storageBlob, "/download/", properties.getExpired());
+    }
+
+    /**
+     * 鏋勫缓甯︾鍚嶇殑URL
+     *
+     * @param storageBlob 鏂囦欢鍏冩暟鎹�
+     * @param actionPath  鎿嶄綔璺緞 "/preview/" or "/download/"
+     * @param expired     杩囨湡鏃堕棿 濡傛灉涓嶉厤缃紝涓嶄紶鍙傦紝灏嗕娇鐢ㄩ粯璁ゅ��120鍒嗛挓
+     * @return 甯︾鍚嶇殑URL
+     */
+    public String buildSignedUrl(StorageBlobVO storageBlob, String actionPath, BigDecimal expired) {
+        if (!Arrays.asList("/preview/", "/download/").contains(actionPath)) {
+            throw new IllegalArgumentException("鎿嶄綔璺緞鍙傛暟閿欒");
+        }
+        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();
+        BigDecimal expiredValue = expired == null ? new BigDecimal("120") : expired;
+        long expiredMillis = expiredValue.multiply(new BigDecimal("60000")).longValue();
+        if (expiredMillis <= 0L) {
+            expiredMillis = 2L * 60L * 60L * 1000L;
+        }
+        Date issuedAt = new Date(now);
+        Date expiration = new Date(now + expiredMillis);
+        SecretKey key = Keys.hmacShaKeyFor(properties.getJwtSecret().getBytes(StandardCharsets.UTF_8));
+
+        String token = Jwts.builder()
+                .subject(storageBlob.getUidFilename())
+                .issuedAt(issuedAt)       // 鏂扮増寤鸿鐩存帴璋冪敤 .issuedAt()
+                .expiration(expiration)   // 鏂扮増寤鸿鐩存帴璋冪敤 .expiration()
+                .claim("path", storageBlob.getPath())
+                .claim("resourceKey", storageBlob.getResourceKey())
+                .signWith(key)            // 閲嶇偣锛氫紶鍏ヤ笂闈㈢敓鎴愮殑 key 瀵硅薄锛岃�屼笉鏄� String
+                .compact();
+        cacheTokenUsage(token, expiredMillis);
+        return baseUrl + "?token=" + token;
+    }
+
+    private void cacheTokenUsage(String token, long expiredMillis) {
+        if (!StringUtils.hasText(token)) {
+            return;
+        }
+        long ttl = expiredMillis > 0L ? expiredMillis : 2L * 60L * 60L * 1000L;
+        stringRedisTemplate.opsForValue().set(buildTokenUsageKey(token), "0", ttl, TimeUnit.MILLISECONDS);
+    }
+
+    private String buildTokenUsageKey(String token) {
+        return TOKEN_USAGE_KEY_PREFIX + token;
+    }
+
+    public String buildRelativePath() {
+        LocalDate now = LocalDate.now();
+        return now.format(YEAR_PATH_FORMATTER) + "/" + now.format(MONTH_DAY_PATH_FORMATTER);
+    }
+
+    public void validateTokenUsage(String token) {
+        String redisKey = buildTokenUsageKey(token);
+        String currentCountValue = stringRedisTemplate.opsForValue().get(redisKey);
+        if (!StringUtils.hasText(currentCountValue)) {
+            throw new IllegalArgumentException("閾炬帴宸茶繃鏈熸垨杈惧埌浣跨敤娆℃暟澶辨晥");
+        }
+        long currentCount = Long.parseLong(currentCountValue);
+        int limit = resolveLimit();
+        if (currentCount >= limit) {
+            stringRedisTemplate.delete(redisKey);
+            throw new IllegalArgumentException("閾炬帴杈惧埌浣跨敤娆℃暟澶辨晥");
+        }
+        Long updatedCount = stringRedisTemplate.opsForValue().increment(redisKey);
+        if (updatedCount != null && updatedCount >= limit) {
+            stringRedisTemplate.delete(redisKey);
+        }
+    }
+
+    private int resolveLimit() {
+        return properties.getUseLimit() == null || properties.getUseLimit() <= 0 ? 10 : properties.getUseLimit();
+    }
+
+    /**
+     * 鍘嬬缉鏂囦欢 鍥剧墖
+     *
+     * @param file 鏂囦欢
+     * @return 鍘嬬缉鍚庣殑鏂囦欢
+     */
+    public File compressFile(File file) {
+        if (properties.getCompress() && isImage(file.getName()) && (file.length() > properties.getNeedCompressSize().toBytes())) {
+            try {
+                // 鍒涘缓涓�涓复鏃舵枃浠跺瓨鏀惧帇缂╁悗鐨勫浘鐗囷紝閬垮厤鐮村潖鍘熷浘
+                File compressedFile = new File(file.getParent(), "thumb_" + file.getName());
+
+                // 1. 濡傛灉宸茬粡瀛樺湪鍘嬬缉杩囩殑鏂囦欢锛岀洿鎺ヨ繑鍥烇紝涓嶅啀娑堣�� CPU 鍘嬬缉
+                if (compressedFile.exists()) {
+                    return compressedFile;
+                }
+
+                // 浣跨敤 Thumbnailator 杩涜鍘嬬缉
+                Thumbnails.of(file)
+                        .scale(1.0f)                // 淇濇寔鍘熷昂瀵�
+                        .outputQuality(properties.getCompressQuality())        // 鏍稿績锛氳缃敾璐� (0.0~1.0)
+                        .toFile(compressedFile);
+
+                return compressedFile;
+            } catch (Exception e) {
+                // 濡傛灉鍘嬬缉澶辫触锛岄檷绾у鐞嗭細杩斿洖鍘熷浘
+                return file;
+            }
+        }
+        return file;
+    }
+    // 绠�鍗曠殑鍚庣紑鍒ゆ柇
+    private boolean isImage(String fileName) {
+        String ext = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
+        return "jpg".equals(ext) || "jpeg".equals(ext) || "png".equals(ext);
+    }
+
+
+}

--
Gitblit v1.9.3