liyong
5 小时以前 1ca5584d7e3200a9af65a099bd26d3593e2ba702
src/main/java/com/ruoyi/basic/service/impl/StorageBlobServiceImpl.java
@@ -1,103 +1,171 @@
package com.ruoyi.basic.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.StorageBlobDTO;
import com.ruoyi.basic.mapper.StorageAttachmentMapper;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.mapper.StorageBlobMapper;
import com.ruoyi.basic.pojo.StorageAttachment;
import com.ruoyi.basic.pojo.StorageBlob;
import com.ruoyi.basic.service.StorageBlobService;
import com.ruoyi.common.exception.file.InvalidExtensionException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MinioUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.framework.web.domain.MinioResult;
import com.ruoyi.basic.utils.FileUtil;
import com.ruoyi.common.config.FileProperties;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.security.Keys;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
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;
import java.util.stream.Collectors;
import java.util.UUID;
/**
 * <p>
 * 通用文件上传的附件信息 服务实现类
 * </p>
 *
 * @author ruoyi
 * @since 2025-05-29
 */
@Service
@RequiredArgsConstructor
public class StorageBlobServiceImpl extends ServiceImpl<StorageBlobMapper, StorageBlob> implements StorageBlobService {
    @Autowired
    private StorageAttachmentMapper storageAttachmentMapper;
    @Autowired
    private StorageBlobMapper storageBlobMapper;
    @Autowired
    private MinioUtils minioUtils;
    private final FileProperties properties;
    private final StorageBlobMapper storageBlobMapper;
    private final FileUtil fileUtil;
    @Override
    public List<StorageBlobDTO> updateStorageBlobs(List<MultipartFile> files, String bucketName) {
        // 若没传入bucketName,则使用默认bucketName
        if (StringUtils.isEmpty(bucketName)) {
            bucketName  = minioUtils.getDefaultBucket();
    public List<StorageBlobVO> upload(List<MultipartFile> files, Boolean isPublic) {
        if (CollectionUtils.isEmpty(files)) {
            throw new IllegalArgumentException("文件不能为空");
        }
        List<StorageBlobDTO> storageBlobDTOs = new ArrayList<>();
        List<StorageBlobVO> storageBlobVOS = new ArrayList<>();
        for (MultipartFile file : files) {
            try {
                MinioResult res = minioUtils.upload(bucketName, file, false);
                StorageBlobDTO dto = new StorageBlobDTO();
                dto.setContentType(file.getContentType());
                dto.setBucketFilename(res.getBucketFileName());
                dto.setOriginalFilename(res.getOriginalName());
                dto.setByteSize(file.getSize());
                dto.setKey(IdUtils.simpleUUID());
                dto.setBucketName(bucketName);
                dto.setCreateTime(DateUtils.getNowDate());
                dto.setUrl(minioUtils.getPreviewUrl(res.getBucketFileName(), bucketName, false));
                // 插入数据库
                storageBlobMapper.insert(dto);
                storageBlobDTOs.add(dto);
            } catch (InvalidExtensionException e) {
                throw new RuntimeException("minio文件上传异常:" +  e);
            if (file == null || file.isEmpty()) {
                throw new IllegalArgumentException("文件不能为空");
            }
            String originalFileName = StringUtils.hasText(file.getOriginalFilename())
                    ? StringUtils.cleanPath(file.getOriginalFilename())
                    : UUID.randomUUID().toString();
            String fileName = UUID.randomUUID() + "_" + originalFileName;
            String relativePath = fileUtil.buildRelativePath();
            File targetDirectory = new File(properties.getPath(), relativePath);
            if (!targetDirectory.exists() && !targetDirectory.mkdirs()) {
                throw new RuntimeException("创建上传目录失败");
            }
            File dest = new File(targetDirectory, fileName);
            StorageBlobVO storageBlob;
            try {
                file.transferTo(dest);
                storageBlob = getStorageBlob(file, originalFileName, fileName, relativePath, isPublic);
                if (storageBlob == null || storageBlob.getId() == null) {
                    throw new RuntimeException("文件元数据保存失败");
                }
            } catch (RuntimeException e) {
                if (dest.exists()) {
                    dest.delete();
                }
                throw e;
            } catch (IOException e) {
                throw new RuntimeException("文件保存失败", e);
            }
            storageBlobVOS.add(storageBlob);
        }
        return storageBlobDTOs;
        return storageBlobVOS;
    }
    @Override
    public int deleteStorageBlobs(StorageAttachment attachment) {
        List<StorageAttachment> attachments = storageAttachmentMapper.selectList(new LambdaQueryWrapper<StorageAttachment>()
                .eq(StorageAttachment::getRecordId, attachment.getRecordId())
                .eq(StorageAttachment::getRecordType, attachment.getRecordType())
                .eq(StorageAttachment::getName, attachment.getName()));
        List<Long> ids = attachments.stream().map(StorageAttachment::getStorageBlobId).collect(Collectors.toList());
        List<StorageBlob> storageBlobs = storageBlobMapper.selectList(new LambdaQueryWrapper<StorageBlob>()
                .in(StorageBlob::getId, ids));
        if (!storageBlobs.isEmpty()) {
            for (StorageBlob storageBlob : storageBlobs) {
                // 移除桶内文件
                minioUtils.removeObjectsResult(storageBlob.getBucketName(), storageBlob.getBucketName());
            }
    public File getFileByToken(String fileName, String token) {
        if (!StringUtils.hasText(token)) {
            throw new IllegalArgumentException("token不能为空");
        }
        if (!ids.isEmpty()) {
            return storageBlobMapper.delete(new QueryWrapper<StorageBlob>().lambda().in(StorageBlob::getId, ids));
        }
        String secretStr = properties.getJwtSecret();
        return 0;
        SecretKey key = Keys.hmacShaKeyFor(secretStr.getBytes(StandardCharsets.UTF_8));
        Claims claims = Jwts.parser()
                .verifyWith(key)          // 代替旧版的 setSigningKey
                .build()                  // 必须先构建解析器
                .parseSignedClaims(token) // 代替旧版的 parseClaimsJws
                .getPayload();            // 代替旧版的 getBody()
        if (!fileName.equals(claims.getSubject())) {
            throw new IllegalArgumentException("token与文件不匹配");
        }
        fileUtil.validateTokenUsage(token);
        StorageBlob storageBlob = findStorageBlob(fileName);
        String path = storageBlob == null ? claims.get("path", String.class) : storageBlob.getPath();
        if (!StringUtils.hasText(path)) {
            return new File(properties.getPath(), fileName);
        }
        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, Boolean isPublic) {
        StorageBlobVO storageBlob = new StorageBlobVO();
        storageBlob.setResourceKey(UUID.randomUUID().toString().replace("-", ""));
        storageBlob.setContentType(file.getContentType());
        storageBlob.setOriginalFilename(originalFileName);
        storageBlob.setUidFilename(fileName);
        storageBlob.setByteSize(file.getSize());
        storageBlob.setPath(relativePath);
        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("文件元数据保存失败");
        }
        return storageBlob;
    }
    @Override
    public String getDownloadFileName(String fileName) {
        StorageBlob storageBlob = findStorageBlob(fileName);
        if (storageBlob == null || !StringUtils.hasText(storageBlob.getOriginalFilename())) {
            return fileName;
        }
        return storageBlob.getOriginalFilename();
    }
}