| | |
| | | import com.ruoyi.basic.dto.StorageAttachmentDTO; |
| | | import com.ruoyi.basic.dto.StorageBlobVO; |
| | | import com.ruoyi.basic.mapper.StorageAttachmentMapper; |
| | | import com.ruoyi.basic.mapper.StorageBlobMapper; |
| | | import com.ruoyi.basic.pojo.StorageAttachment; |
| | | import com.ruoyi.common.config.FileProperties; |
| | | import io.jsonwebtoken.Jwts; |
| | | import io.jsonwebtoken.SignatureAlgorithm; |
| | | 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.*; |
| | |
| | | public class FileUtil { |
| | | private final FileProperties properties; |
| | | private final StorageAttachmentMapper storageAttachmentMapper; |
| | | private final StorageBlobMapper storageBlobMapper; |
| | | private final StringRedisTemplate stringRedisTemplate; |
| | | |
| | | private static final String TOKEN_USAGE_KEY_PREFIX = "file:token:usage:"; |
| | |
| | | } |
| | | 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() |
| | | .setSubject(storageBlob.getUidFilename()) |
| | | .setIssuedAt(issuedAt) |
| | | .setExpiration(expiration) |
| | | .subject(storageBlob.getUidFilename()) |
| | | .issuedAt(issuedAt) // 新版建议直接调用 .issuedAt() |
| | | .expiration(expiration) // 新版建议直接调用 .expiration() |
| | | .claim("path", storageBlob.getPath()) |
| | | .claim("resourceKey", storageBlob.getResourceKey()) |
| | | .signWith(SignatureAlgorithm.HS256, properties.getJwtSecret()) |
| | | .signWith(key) // 重点:传入上面生成的 key 对象,而不是 String |
| | | .compact(); |
| | | cacheTokenUsage(token, expiredMillis); |
| | | String domain = StringUtils.trimTrailingCharacter(properties.getDomain(), '/'); |
| | |
| | | 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); |
| | | } |
| | | |
| | | |
| | | } |