2025-03-10 18:06:07 +08:00
|
|
|
|
package edu.whut.smilepicturebackend.manager.upload;
|
2025-03-14 16:55:32 +08:00
|
|
|
|
import cn.hutool.core.collection.CollUtil;
|
2025-03-10 18:06:07 +08:00
|
|
|
|
import cn.hutool.core.date.DateUtil;
|
|
|
|
|
import cn.hutool.core.io.FileUtil;
|
|
|
|
|
import cn.hutool.core.util.NumberUtil;
|
|
|
|
|
import cn.hutool.core.util.RandomUtil;
|
|
|
|
|
import com.qcloud.cos.model.PutObjectResult;
|
|
|
|
|
import com.qcloud.cos.model.ciModel.persistence.CIObject;
|
|
|
|
|
import com.qcloud.cos.model.ciModel.persistence.ImageInfo;
|
2025-03-14 16:55:32 +08:00
|
|
|
|
import com.qcloud.cos.model.ciModel.persistence.ProcessResults;
|
2025-03-10 18:06:07 +08:00
|
|
|
|
import edu.whut.smilepicturebackend.config.CosClientConfig;
|
|
|
|
|
import edu.whut.smilepicturebackend.exception.BusinessException;
|
|
|
|
|
import edu.whut.smilepicturebackend.exception.ErrorCode;
|
|
|
|
|
import edu.whut.smilepicturebackend.manager.CosManager;
|
|
|
|
|
import edu.whut.smilepicturebackend.model.file.UploadPictureResult;
|
|
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.util.Date;
|
2025-03-14 16:55:32 +08:00
|
|
|
|
import java.util.List;
|
2025-03-10 18:06:07 +08:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 图片上传模板
|
|
|
|
|
*/
|
|
|
|
|
@Slf4j
|
|
|
|
|
public abstract class PictureUploadTemplate {
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private CosClientConfig cosClientConfig;
|
|
|
|
|
|
|
|
|
|
@Resource
|
|
|
|
|
private CosManager cosManager;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 上传图片
|
|
|
|
|
*
|
|
|
|
|
* @param inputSource 文件
|
|
|
|
|
* @param uploadPathPrefix 上传路径前缀
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public UploadPictureResult uploadPicture(Object inputSource, String uploadPathPrefix) {
|
|
|
|
|
// 1. 校验图片
|
|
|
|
|
validPicture(inputSource);
|
|
|
|
|
// 2. 图片上传地址
|
|
|
|
|
String uuid = RandomUtil.randomString(16);
|
|
|
|
|
String originalFilename = getOriginFilename(inputSource);
|
2025-03-14 16:55:32 +08:00
|
|
|
|
// extName 直接取扩展名,不含点
|
|
|
|
|
String extension = FileUtil.extName(originalFilename); // "png" 或 "jpg"
|
2025-03-10 18:06:07 +08:00
|
|
|
|
// 自己拼接文件上传路径,而不是使用原始文件名称,可以增强安全性
|
2025-03-14 16:55:32 +08:00
|
|
|
|
String uploadFilename = String.format("%s_%s.%s",
|
|
|
|
|
DateUtil.formatDate(new Date()),
|
|
|
|
|
uuid,
|
|
|
|
|
extension);
|
2025-03-10 18:06:07 +08:00
|
|
|
|
//如果多个项目共享存储桶,请在桶的根目录下以各项目名作为目录。
|
|
|
|
|
String projectName="smile-picture";
|
|
|
|
|
String uploadPath = String.format("/%s/%s/%s",projectName, uploadPathPrefix, uploadFilename);
|
|
|
|
|
File file = null;
|
|
|
|
|
try {
|
|
|
|
|
// 3. 创建临时文件,获取文件到服务器
|
|
|
|
|
file = File.createTempFile(uploadPath, null);
|
|
|
|
|
// 处理文件来源
|
|
|
|
|
processFile(inputSource, file);
|
|
|
|
|
// 4. 上传图片到对象存储
|
|
|
|
|
PutObjectResult putObjectResult = cosManager.putPictureObject(uploadPath, file);
|
|
|
|
|
// 5. 获取图片信息对象,封装返回结果
|
|
|
|
|
ImageInfo imageInfo = putObjectResult.getCiUploadResult().getOriginalInfo().getImageInfo();
|
2025-03-14 16:55:32 +08:00
|
|
|
|
// 获取到图片处理结果
|
|
|
|
|
ProcessResults processResults = putObjectResult.getCiUploadResult().getProcessResults();
|
|
|
|
|
List<CIObject> objectList = processResults.getObjectList();
|
|
|
|
|
if (CollUtil.isNotEmpty(objectList)) {
|
|
|
|
|
// 获取压缩之后得到的文件信息
|
|
|
|
|
CIObject compressedCiObject = objectList.get(0); //第一个是压缩后的
|
|
|
|
|
// 缩略图默认等于压缩图,压缩图是必有的
|
|
|
|
|
CIObject thumbnailCiObject = compressedCiObject;
|
|
|
|
|
// 有生成缩略图,才获取缩略图
|
|
|
|
|
if (objectList.size() > 1) { //第二个是缩略图
|
|
|
|
|
thumbnailCiObject = objectList.get(1);
|
|
|
|
|
}
|
|
|
|
|
// 封装压缩图的返回结果
|
|
|
|
|
return buildResult(originalFilename, compressedCiObject, thumbnailCiObject, imageInfo,uploadPath);
|
|
|
|
|
}
|
2025-03-10 18:06:07 +08:00
|
|
|
|
return buildResult(originalFilename, file, uploadPath, imageInfo);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("图片上传到对象存储失败", e);
|
|
|
|
|
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败");
|
|
|
|
|
} finally {
|
|
|
|
|
// 6. 临时文件清理
|
|
|
|
|
this.deleteTempFile(file);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 校验输入源(本地文件或 URL)
|
|
|
|
|
*/
|
|
|
|
|
protected abstract void validPicture(Object inputSource);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取输入源的原始文件名
|
|
|
|
|
*/
|
|
|
|
|
protected abstract String getOriginFilename(Object inputSource);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理输入源并生成本地临时文件
|
|
|
|
|
*/
|
|
|
|
|
protected abstract void processFile(Object inputSource, File file) throws Exception;
|
|
|
|
|
|
|
|
|
|
|
2025-03-14 16:55:32 +08:00
|
|
|
|
/**
|
|
|
|
|
* 封装返回结果
|
|
|
|
|
*
|
|
|
|
|
* @param originalFilename 原始文件名
|
|
|
|
|
* @param compressedCiObject 压缩后的对象
|
|
|
|
|
* @param thumbnailCiObject 缩略图对象
|
|
|
|
|
* @param imageInfo 图片信息
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
private UploadPictureResult buildResult(String originalFilename, CIObject compressedCiObject, CIObject thumbnailCiObject,
|
|
|
|
|
ImageInfo imageInfo,String uploadPath) {
|
|
|
|
|
// 计算宽高
|
|
|
|
|
int picWidth = compressedCiObject.getWidth();
|
|
|
|
|
int picHeight = compressedCiObject.getHeight();
|
|
|
|
|
double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();
|
|
|
|
|
// 封装返回结果
|
|
|
|
|
UploadPictureResult uploadPictureResult = new UploadPictureResult();
|
|
|
|
|
// 设置压缩后的原图地址
|
|
|
|
|
uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + compressedCiObject.getKey());
|
|
|
|
|
uploadPictureResult.setName(FileUtil.mainName(originalFilename));
|
|
|
|
|
uploadPictureResult.setPicSize(compressedCiObject.getSize().longValue());
|
|
|
|
|
uploadPictureResult.setPicWidth(picWidth);
|
|
|
|
|
uploadPictureResult.setPicHeight(picHeight);
|
|
|
|
|
uploadPictureResult.setPicScale(picScale);
|
|
|
|
|
uploadPictureResult.setPicFormat(compressedCiObject.getFormat());
|
|
|
|
|
uploadPictureResult.setPicColor(imageInfo.getAve());
|
|
|
|
|
// 设置缩略图地址
|
|
|
|
|
uploadPictureResult.setThumbnailUrl(cosClientConfig.getHost() + "/" + thumbnailCiObject.getKey());
|
|
|
|
|
uploadPictureResult.setOriginalUrl(cosClientConfig.getHost() + "/" + uploadPath);
|
|
|
|
|
// 返回可访问的地址
|
|
|
|
|
return uploadPictureResult;
|
|
|
|
|
}
|
2025-03-10 18:06:07 +08:00
|
|
|
|
/**
|
|
|
|
|
* 封装返回结果
|
|
|
|
|
*
|
|
|
|
|
* @param originalFilename
|
|
|
|
|
* @param file
|
|
|
|
|
* @param uploadPath
|
|
|
|
|
* @param imageInfo 对象存储返回的图片信息
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
2025-03-14 16:55:32 +08:00
|
|
|
|
|
2025-03-10 18:06:07 +08:00
|
|
|
|
private UploadPictureResult buildResult(String originalFilename, File file, String uploadPath, ImageInfo imageInfo) {
|
|
|
|
|
// 计算宽高
|
|
|
|
|
int picWidth = imageInfo.getWidth();
|
|
|
|
|
int picHeight = imageInfo.getHeight();
|
|
|
|
|
double picScale = NumberUtil.round(picWidth * 1.0 / picHeight, 2).doubleValue();
|
|
|
|
|
// 封装返回结果
|
|
|
|
|
UploadPictureResult uploadPictureResult = new UploadPictureResult();
|
|
|
|
|
uploadPictureResult.setUrl(cosClientConfig.getHost() + "/" + uploadPath);
|
|
|
|
|
uploadPictureResult.setName(FileUtil.mainName(originalFilename));
|
|
|
|
|
uploadPictureResult.setPicSize(FileUtil.size(file));
|
|
|
|
|
uploadPictureResult.setPicWidth(picWidth);
|
|
|
|
|
uploadPictureResult.setPicHeight(picHeight);
|
|
|
|
|
uploadPictureResult.setPicScale(picScale);
|
|
|
|
|
uploadPictureResult.setPicFormat(imageInfo.getFormat());
|
|
|
|
|
uploadPictureResult.setPicColor(imageInfo.getAve());
|
|
|
|
|
// 返回可访问的地址
|
|
|
|
|
return uploadPictureResult;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 清理临时文件
|
|
|
|
|
*
|
|
|
|
|
* @param file
|
|
|
|
|
*/
|
|
|
|
|
public void deleteTempFile(File file) {
|
|
|
|
|
if (file == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// 删除临时文件
|
|
|
|
|
boolean deleteResult = file.delete();
|
|
|
|
|
if (!deleteResult) {
|
|
|
|
|
log.error("file delete error, filepath = {}", file.getAbsolutePath());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|