package edu.whut.smilepicturebackend.manager.upload; import cn.hutool.core.collection.CollUtil; 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; import com.qcloud.cos.model.ciModel.persistence.ProcessResults; 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.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import javax.annotation.Resource; import java.io.File; import java.util.Date; import java.util.List; /** * 图片上传模板 */ @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); // extName 直接取扩展名,不含点 String extension = FileUtil.extName(originalFilename); // "png" 或 "jpg" // 自己拼接文件上传路径,而不是使用原始文件名称,可以增强安全性 String uploadFilename = String.format("%s_%s.%s", DateUtil.formatDate(new Date()), uuid, extension); //如果多个项目共享存储桶,请在桶的根目录下以各项目名作为目录。 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(); // 获取到图片处理结果 ProcessResults processResults = putObjectResult.getCiUploadResult().getProcessResults(); List 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); } 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; /** * 封装返回结果 * * @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; } /** * 封装返回结果 * * @param originalFilename * @param file * @param uploadPath * @param imageInfo 对象存储返回的图片信息 * @return */ 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()); } } }