chenhj
2026-04-24 54007fa3b5f4a280b0228c87387a0a65ee677b74
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package com.ruoyi.basic.service.impl;
 
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.basic.dto.StorageBlobVO;
import com.ruoyi.basic.mapper.StorageBlobMapper;
import com.ruoyi.basic.pojo.StorageBlob;
import com.ruoyi.basic.service.StorageBlobService;
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.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.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
 
/**
 * <p>
 * 通用文件上传的附件信息 服务实现类
 * </p>
 *
 */
@Service
@RequiredArgsConstructor
public class StorageBlobServiceImpl extends ServiceImpl<StorageBlobMapper, StorageBlob> implements StorageBlobService {
    private final FileProperties properties;
    private final StorageBlobMapper storageBlobMapper;
    private final FileUtil fileUtil;
 
    @Override
    public List<StorageBlobVO> upload(List<MultipartFile> files) {
        if (CollectionUtils.isEmpty(files)) {
            throw new IllegalArgumentException("文件不能为空");
        }
 
        List<StorageBlobVO> storageBlobVOS = new ArrayList<>();
 
        for (MultipartFile file : files) {
            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);
                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 storageBlobVOS;
    }
 
    @Override
    public File getFileByToken(String fileName, String token) {
        if (!StringUtils.hasText(token)) {
            throw new IllegalArgumentException("token不能为空");
        }
 
        String secretStr = properties.getJwtSecret();
 
        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);
    }
 
    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) {
        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);
        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();
    }
}