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;
/**
*
* 通用文件上传的附件信息 服务实现类
*
*
*/
@Service
@RequiredArgsConstructor
public class StorageBlobServiceImpl extends ServiceImpl implements StorageBlobService {
private final FileProperties properties;
private final StorageBlobMapper storageBlobMapper;
private final FileUtil fileUtil;
@Override
public List upload(List files) {
if (CollectionUtils.isEmpty(files)) {
throw new IllegalArgumentException("文件不能为空");
}
List 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 = StringUtils.hasText(properties.getJwtSecret())
? properties.getJwtSecret()
: "local-file-jwt-secret";
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()
.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();
}
}