minio整理,一些返回逻辑修改

This commit is contained in:
litianxiang
2025-10-23 17:30:57 +08:00
parent 336a84faa8
commit c983261723
13 changed files with 355 additions and 370 deletions

View File

@@ -27,16 +27,16 @@ public class TryOnEffectController {
private final TryOnEffectService tryOnEffectService;
@Operation(summary = "生成试穿效果", description = "根据顾客照片和模特照片生成试穿效果")
@Operation(summary = "生成试穿效果", description = "根据服装,模特照片生成试穿效果其中styleId是必选当二次生成时要带上相关参数比如顾客照片")
@PostMapping("/generate")
public ApiResponse<String> generateTryOnEffect(
public ApiResponse<TryOnResultVo> generateTryOnEffect(
@Parameter(description = "试穿效果请求参数", required = true)
@Valid @RequestBody TryOnEffect tryOnEffectDto) {
String taskId = tryOnEffectService.generateTryOnEffect(tryOnEffectDto);
return ApiResponse.success(taskId);
TryOnResultVo tryOnResultVo = tryOnEffectService.generateTryOnEffect(tryOnEffectDto);
return ApiResponse.success(tryOnResultVo);
}
@Operation(summary = "获取收藏的试穿效果", description = "获取指定进店记录的收藏的试穿效果")
@Operation(summary = "获取收藏的试穿效果", description = "对应library页面点击details后的显示参数为进店记录id")
@GetMapping("/favorites/{visitRecordId}")
public ApiResponse<List<TryOnResultVo>> getFavoriteTryOnEffects(
@Parameter(description = "进店记录ID", required = true)
@@ -46,7 +46,7 @@ public class TryOnEffectController {
}
@GetMapping("/style/{styleId}")
@Operation(summary = "获取某套服装的所有生成结果", description = "获取某套服装的所有生成结果")
@Operation(summary = "获取某套服装的所有生成结果", description = "对应customize your look页面点击finish后的显示")
public ApiResponse<List<TryOnResultVo>> getTryOnEffectsByStyleId(
@Parameter(description = "服装ID", required = true)
@PathVariable Long styleId) {

View File

@@ -5,6 +5,7 @@ import com.aida.lanecarford.entity.VisitRecord;
import com.aida.lanecarford.service.VisitRecordService;
import com.aida.lanecarford.vo.LibraryVo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
@@ -47,6 +48,7 @@ public class VisitRecordController {
}
//根据顾客ID查询所有进店记录
@GetMapping("/customer/{customerId}")
@Operation(summary = "根据顾客ID查询所有进店记录",description = "打开libiary页面调用这个接口参数为当前顾客的id")
public ApiResponse<List<LibraryVo>> getByCustomerId(@PathVariable Long customerId) {
log.info("开始查询顾客ID为{}的进店记录", customerId);

View File

@@ -1,12 +1,15 @@
package com.aida.lanecarford.dto;
import com.aida.lanecarford.entity.CustomerPhoto;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
@Data
@Schema(description = "顾客照片数据传输对象")
public class CustomerPhotoDto extends CustomerPhoto {
@NotNull(message = "file.cannot.be.empty")
@Schema(description = "上传的照片文件", required = true, type = "string", format = "binary")
private MultipartFile file;
}
}

View File

@@ -1,6 +1,7 @@
package com.aida.lanecarford.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -21,29 +22,34 @@ import java.time.LocalDateTime;
@Accessors(chain = true)
@EqualsAndHashCode(callSuper = true)
@TableName("customer_photos")
@Schema(description = "顾客照片信息")
public class CustomerPhoto extends BaseEntity {
/**
* 顾客ID
*/
@Schema(description = "顾客ID", example = "1", required = true)
@TableField("customer_id")
private Long customerId;
/**
* 进店记录ID
*/
@Schema(description = "进店记录ID", example = "1", required = true)
@TableField("visit_record_id")
private Long visitRecordId;
/**
* 照片URL
*/
@Schema(description = "照片URL", example = "https://example.com/photo.jpg", required = true)
@TableField("photo_url")
private String photoUrl;
/**
* 是否为主照片(0-否,1-是)
*/
@Schema(description = "是否为主照片", example = "1", allowableValues = {"0", "1"})
@TableField("is_primary")
private Integer isPrimary;

View File

@@ -1,6 +1,7 @@
package com.aida.lanecarford.entity;
import com.baomidou.mybatisplus.annotation.*;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
@@ -26,78 +27,91 @@ public class TryOnEffect extends BaseEntity {
/**
* 顾客ID
*/
@Schema(description = "顾客ID", example = "1", required = true)
@TableField("customer_id")
private Long customerId;
/**
* 进店记录ID
*/
@Schema(description = "进店记录ID", example = "1", required = true)
@TableField("visit_record_id")
private Long visitRecordId;
/**
* 风格ID
* 衣服搭配ID,当is_regenerated为0时才会有值
*/
@Schema(description = "衣服搭配ID,当is_regenerated为0时才会有值", example = "1", required = true)
@TableField("style_id")
private Long styleId;
/**
* 顾客照片ID
* 顾客照片ID,当is_regenerated为1时才会有值
*/
@Schema(description = "顾客照片ID,当is_regenerated为1时才会有值", example = "1", required = true)
@TableField("customer_photo_id")
private Long customerPhotoId;
/**
* 模特照片ID
*/
@Schema(description = "模特照片ID目前没有这个功能", example = "1", required = true)
@TableField("model_photo_id")
private Long modelPhotoId;
/**
* 提示词,当is_regenerated为1时才会有值
*/
@Schema(description = "提示词,当is_regenerated为1时才会有值", example = "1", required = true)
@TableField("prompt")
private String prompt;
/**
* 原试穿效果ID,当is_regenerated为1时才会有值
*/
@Schema(description = "原试穿效果ID,当is_regenerated为1时才会有值", example = "1", required = true)
@TableField("original_try_on_id")
private Long originalTryOnId;
/**
* 是否由生成结果重新生成(0-否,1-是)
*/
@Schema(description = "是否由生成结果重新生成(0-否,1-是)", example = "1", required = true)
@TableField("is_regenerated")
private Integer isRegenerated;
/**
* 试穿结果图片URL
*/
@Schema(description = "试穿结果图片URL", example = "1", required = false)
@TableField("result_image_url")
private String resultImageUrl;
/**
* 请求ID
*/
@Schema(description = "请求ID", example = "1", required = false)
@TableField("request_id")
private String requestId;
/**
* 生成状态(pending-等待中,processing-处理中,completed-已完成,failed-失败)
*/
@Schema(description = "生成状态(pending-等待中,processing-处理中,completed-已完成,failed-失败)", example = "1", required = false)
@TableField("generation_status")
private String generationStatus;
/**
* 错误信息
*/
@Schema(description = "错误信息", example = "1", required = false)
@TableField("error_message")
private String errorMessage;
/**
* 是否喜欢的最终造型(0-否,1-是)
* 是否喜欢(0-否,1-是)
*/
@Schema(description = "是否喜欢(0-否,1-是)", example = "1", required = false)
@TableField("is_favorite")
private Integer isFavorite;

View File

@@ -17,13 +17,4 @@ public interface ImageCompositionService {
* @return 合成后图片的MinIO访问URL
*/
String composeAndUploadImages(List<String> imageUrls);
/**
* 合成图片并上传到指定存储桶
*
* @param imageUrls 图片URL列表1-3张
* @param bucketName 存储桶名称
* @return 合成后图片的MinIO访问URL
*/
String composeAndUploadImages(List<String> imageUrls, String bucketName);
}

View File

@@ -15,7 +15,7 @@ import java.util.List;
*/
public interface TryOnEffectService extends IService<TryOnEffect> {
String generateTryOnEffect(@Valid TryOnEffect tryOnEffectDto);
TryOnResultVo generateTryOnEffect(@Valid TryOnEffect tryOnEffectDto);
List<TryOnResultVo> getFavoriteTryOnEffects(Long visitRecordId);

View File

@@ -1,5 +1,6 @@
package com.aida.lanecarford.service.impl;
import com.aida.lanecarford.constant.MinioFileConstants;
import com.aida.lanecarford.dto.CustomerPhotoDto;
import com.aida.lanecarford.entity.CustomerPhoto;
import com.aida.lanecarford.mapper.CustomerPhotoMapper;
@@ -23,13 +24,16 @@ public class CustomerPhotoServiceImpl extends ServiceImpl<CustomerPhotoMapper, C
@Override
public CustomerPhoto upload(CustomerPhotoDto customerPhotoDto) {
//TODO:设置桶名
String url = minioUtil.uploadFile(customerPhotoDto.getFile());
String logicalUrl = minioUtil.uploadFile(
customerPhotoDto.getFile(),
MinioFileConstants.FileType.CUSTOMER_PHOTO
);
CustomerPhoto customerPhoto = CopyUtil.copyObject(customerPhotoDto, CustomerPhoto.class);
customerPhoto.setPhotoUrl(url);
this.save(customerPhoto);
customerPhoto.setPhotoUrl(logicalUrl);
this.save(customerPhoto);
return customerPhoto;
}

View File

@@ -1,5 +1,7 @@
package com.aida.lanecarford.service.impl;
import com.aida.lanecarford.config.MinioConfig;
import com.aida.lanecarford.constant.MinioFileConstants;
import com.aida.lanecarford.service.ImageCompositionService;
import com.aida.lanecarford.util.ImageCompositionUtil;
import com.aida.lanecarford.util.MinioUtil;
@@ -23,16 +25,18 @@ public class ImageCompositionServiceImpl implements ImageCompositionService {
private final ImageCompositionUtil imageCompositionUtil;
private final MinioUtil minioUtil;
/**
* 合成并上传图片
*/
@Override
public String composeAndUploadImages(List<String> imageUrls) {
return composeAndUploadImages(imageUrls, null);
}
@Override
public String composeAndUploadImages(List<String> imageUrls, String bucketName) {
try {
log.info("开始合成并上传图片,图片数量: {}, 存储桶: {}",
imageUrls != null ? imageUrls.size() : 0, bucketName);
log.info("开始合成并上传图片,图片数量: {}",
imageUrls != null ? imageUrls.size() : 0);
// 参数验证
if (imageUrls == null || imageUrls.isEmpty()) {
@@ -59,19 +63,15 @@ public class ImageCompositionServiceImpl implements ImageCompositionService {
// 合成图片
byte[] composedImageBytes = imageCompositionUtil.composeImages(validUrls);
// 生成文件名
String fileName = imageCompositionUtil.generateComposedFileName(validUrls);
// 使用新的上传方法返回逻辑URL
String logicalUrl = minioUtil.uploadBytes(
composedImageBytes,
MinioFileConstants.FileType.COMPOSED_IMAGE,
"image/jpeg"
);
// 上传到MinIO
String uploadedUrl;
if (bucketName != null && !bucketName.trim().isEmpty()) {
uploadedUrl = minioUtil.uploadBytes(composedImageBytes, fileName, "image/jpeg", bucketName);
} else {
uploadedUrl = minioUtil.uploadBytes(composedImageBytes, fileName, "image/jpeg");
}
log.info("图片合成并上传成功访问URL: {}", uploadedUrl);
return uploadedUrl;
log.info("图片合成并上传成功逻辑URL: {}", logicalUrl);
return logicalUrl;
} catch (Exception e) {
log.error("图片合成并上传失败: {}", e.getMessage(), e);

View File

@@ -3,6 +3,7 @@ package com.aida.lanecarford.service.impl;
import cn.hutool.json.JSONObject;
import com.aida.lanecarford.common.CommonConstant;
import com.aida.lanecarford.common.response.ResultEnum;
import com.aida.lanecarford.constant.MinioFileConstants;
import com.aida.lanecarford.entity.CustomerPhoto;
import com.aida.lanecarford.entity.ModelPhoto;
import com.aida.lanecarford.entity.Style;
@@ -53,9 +54,9 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
private final MinioUtil minioUtil;
@Override
public String generateTryOnEffect(TryOnEffect tryOnEffectDto) {
public TryOnResultVo generateTryOnEffect(TryOnEffect tryOnEffectDto) {
Integer isRegenerated = tryOnEffectDto.getIsRegenerated();
String toAIUrl = null;
String toAIlogicalUrl = null;
String prompt = null;
// 收集图片URL
List<String> imageUrls = new ArrayList<>();
@@ -64,7 +65,7 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
Long originalTryOnId = tryOnEffectDto.getOriginalTryOnId();
// 验证originalTryOnId不能为空
if (originalTryOnId == null) {
throw new RuntimeException("originalTryOnId cannot be null");
throw BusinessException.parameterRequired("originalTryOnId");
}
TryOnEffect originalTryOn = this.getById(originalTryOnId);
String resultImageUrl = originalTryOn.getResultImageUrl();
@@ -83,7 +84,7 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
Long styleId = tryOnEffectDto.getStyleId();
// 验证styleId不能为空
if (styleId == null) {
throw new RuntimeException("styleId cannot be null");
throw BusinessException.parameterRequired("styleId");
}
//根据id查到对应styleurl
Style style = styleService.getById(styleId);
@@ -91,7 +92,7 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
if (styleImageUrl != null && !styleImageUrl.trim().isEmpty()) {
imageUrls.add(styleImageUrl);
}
Long modelPhotoId = tryOnEffectDto.getModelPhotoId();
if (modelPhotoId != null) {
//根据id查到对应modelurl
@@ -103,32 +104,44 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
}
// 合成图片
if (!imageUrls.isEmpty()) {
if (!imageUrls.isEmpty() && imageUrls.size() > 1) {
log.info("开始合成图片,图片数量: {}", imageUrls.size());
try {
toAIUrl = imageCompositionService.composeAndUploadImages(imageUrls);
log.info("图片合成成功合成图片URL: {}", toAIUrl);
// 将逻辑URL批量转换为预签名URL以便图像合成服务可以访问
List<String> presignedUrls = minioUtil.convertToPresignedUrls(imageUrls, CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
log.debug("批量转换逻辑URL为预签名URL数量: {}", presignedUrls.size());
toAIlogicalUrl = imageCompositionService.composeAndUploadImages(presignedUrls);
log.info("图片合成成功合成图片URL: {}", toAIlogicalUrl);
} catch (Exception e) {
log.error("图片合成失败: {}", e.getMessage(), e);
throw new RuntimeException("image error " + e.getMessage(), e);
}
} else if (imageUrls.size() == 1) {
toAIlogicalUrl = imageUrls.get(0);
} else {
log.warn("没有找到有效的图片URL进行合成");
throw new RuntimeException("image cannot be null");
throw BusinessException.parameterRequired("image");
}
}
//调用模型生成试穿效果
log.info("准备调用第三方AI服务输入图片URL: {}", toAIUrl);
String AIRreultUrl = AITryOnEffect(prompt, toAIUrl);
log.info("准备调用第三方AI服务输入图片URL: {}", toAIlogicalUrl);
String AIRreultlogicalUrl = AITryOnEffect(prompt, toAIlogicalUrl);
tryOnEffectDto.setResultImageUrl(AIRreultUrl);
tryOnEffectDto.setResultImageUrl(AIRreultlogicalUrl);
this.saveOrUpdate(tryOnEffectDto);
TryOnResultVo tryOnResultVo = new TryOnResultVo();
tryOnResultVo.setTryOnId(tryOnEffectDto.getId());
return "taskId";
tryOnResultVo.setTryOnUrl(minioUtil.convertToPresignedUrl(AIRreultlogicalUrl, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
return tryOnResultVo;
}
//library页面点击details后的显示
@Override
public List<TryOnResultVo> getFavoriteTryOnEffects(Long visitRecordId) {
List<TryOnEffect> tryOnEffects = this.list(new LambdaQueryWrapper<TryOnEffect>()
@@ -138,17 +151,18 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
for (TryOnEffect tryOnEffect : tryOnEffects) {
TryOnResultVo tryOnResultVo = new TryOnResultVo();
tryOnResultVo.setTryOnId(tryOnEffect.getId());
tryOnResultVo.setTryOnUrl(minioUtil.getPresignedUrl(
// 使用新的API获取预签名URL数据库存储的是逻辑URL
tryOnResultVo.setTryOnUrl(minioUtil.convertToPresignedUrl(
tryOnEffect.getResultImageUrl(),
CommonConstant.MINIO_IMAGE_EXPIRE_TIME
));
// 如果是原始效果则获取对应的style图片
if (tryOnEffect.getIsRegenerated()==0){
if (tryOnEffect.getIsRegenerated() == 0) {
LambdaQueryWrapper<Style> styleLambdaQueryWrapper = new LambdaQueryWrapper<>();
styleLambdaQueryWrapper.eq(Style::getId, tryOnEffect.getStyleId()).select(Style::getStyleImageUrl);
Style style = styleService.getOne(styleLambdaQueryWrapper);
tryOnResultVo.setStyleUrl(minioUtil.getPresignedUrl(
style.getStyleImageUrl(),
tryOnResultVo.setStyleUrl(minioUtil.convertToPresignedUrl(
style.getStyleImageUrl(),
CommonConstant.MINIO_IMAGE_EXPIRE_TIME
));
}
@@ -160,6 +174,7 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
return tryOnResultVos;
}
//目前用于customize your look页面点击finish后的显示
@Override
public List<TryOnResultVo> getTryOnEffectsByStyleId(Long styleId) {
List<TryOnEffect> tryOnEffects = this.list(new LambdaQueryWrapper<TryOnEffect>()
@@ -168,19 +183,20 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
for (TryOnEffect tryOnEffect : tryOnEffects) {
TryOnResultVo tryOnResultVo = new TryOnResultVo();
tryOnResultVo.setTryOnId(tryOnEffect.getId());
tryOnResultVo.setTryOnUrl(minioUtil.getPresignedUrl(
tryOnEffect.getResultImageUrl(),
CommonConstant.MINIO_IMAGE_EXPIRE_TIME
));
// 使用新的API获取预签名URL数据库存储的是逻辑URL
tryOnResultVo.setTryOnUrl(minioUtil.convertToPresignedUrl(
tryOnEffect.getResultImageUrl(),
CommonConstant.MINIO_IMAGE_EXPIRE_TIME
));
// 如果是原始效果则获取对应的style图片
if (tryOnEffect.getIsRegenerated()==0){
if (tryOnEffect.getIsRegenerated() == 0) {
LambdaQueryWrapper<Style> styleLambdaQueryWrapper = new LambdaQueryWrapper<>();
styleLambdaQueryWrapper.eq(Style::getId, tryOnEffect.getStyleId()).select(Style::getStyleImageUrl);
Style style = styleService.getOne(styleLambdaQueryWrapper);
tryOnResultVo.setStyleUrl(minioUtil.getPresignedUrl(
style.getStyleImageUrl(),
CommonConstant.MINIO_IMAGE_EXPIRE_TIME
));
tryOnResultVo.setStyleUrl(minioUtil.convertToPresignedUrl(
style.getStyleImageUrl(),
CommonConstant.MINIO_IMAGE_EXPIRE_TIME
));
}
tryOnResultVo.setIsRegenerated(tryOnEffect.getIsRegenerated());
@@ -195,12 +211,12 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
if (tryOnId == null) {
throw new BusinessException("TryOn ID is required", "试穿效果ID不能为空", ResultEnum.PARAMETER_ERROR.getCode());
}
TryOnEffect tryOnEffect = this.getById(tryOnId);
if (tryOnEffect == null) {
throw new BusinessException("TryOn effect not found", "试穿效果不存在", ResultEnum.FAIL.getCode());
}
// 设置为收藏
tryOnEffect.setIsFavorite(1);
this.updateById(tryOnEffect);
@@ -212,12 +228,12 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
if (tryOnId == null) {
throw new BusinessException("TryOn ID is required", "试穿效果ID不能为空", ResultEnum.PARAMETER_ERROR.getCode());
}
TryOnEffect tryOnEffect = this.getById(tryOnId);
if (tryOnEffect == null) {
throw new BusinessException("TryOn effect not found", "试穿效果不存在", ResultEnum.FAIL.getCode());
}
// 取消收藏
tryOnEffect.setIsFavorite(0);
this.updateById(tryOnEffect);
@@ -225,50 +241,49 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
}
public String AITryOnEffect(String prompt, String url) {
log.info("开始执行AITryOnEffect - prompt: {}, url: {}", prompt, url);
// 参数验证
if (prompt == null || prompt.trim().isEmpty()) {
log.error("参数验证失败 - prompt不能为空");
throw new BusinessException("Prompt is required", "prompt不能为空", ResultEnum.PARAMETER_ERROR.getCode());
}
if (url == null || url.trim().isEmpty()) {
log.error("参数验证失败 - url不能为空");
throw new BusinessException("URL is required", "url不能为空", ResultEnum.PARAMETER_ERROR.getCode());
}
// 获取图片的base64编码
String base64Image = getImageAsBase64(url);
// 调用谷歌API进行图生图
String resultImageUrl = callGoogleImageGenerationAPI(prompt, base64Image);
log.info("AITryOnEffect执行成功结果URL: {}", resultImageUrl);
return resultImageUrl;
}
/**
* 获取图片的base64编码
*/
private String getImageAsBase64(String imageUrl) {
try {
// 从MinIO下载图片并转换为Base64
InputStream inputStream = minioUtil.downloadFile(imageUrl);
byte[] imageBytes = inputStream.readAllBytes();
inputStream.close();
// 转换为Base64编码
String base64 = java.util.Base64.getEncoder().encodeToString(imageBytes);
return base64;
log.info("开始下载图片并转换为Base64: {}", imageUrl);
// 使用新的下载方法,imageUrl是逻辑URL
try (InputStream inputStream = minioUtil.downloadFile(imageUrl)) {
byte[] imageBytes = inputStream.readAllBytes();
String base64 = java.util.Base64.getEncoder().encodeToString(imageBytes);
log.info("图片转换为Base64成功长度: {}", base64.length());
return base64;
}
} catch (Exception e) {
log.error("获取图片base64编码失败: {}", e.getMessage(), e);
throw new BusinessException("Image processing failed", "图片处理失败", ResultEnum.ERROR.getCode());
log.error("下载图片或转换Base64失败: {}", e.getMessage(), e);
throw new BusinessException("Image download failed", "图片下载失败", ResultEnum.ERROR.getCode());
}
}
/**
* 调用谷歌API进行图像生成
*/
@@ -284,16 +299,16 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
"https://aiplatform.googleapis.com/v1/projects/%s/locations/%s/publishers/google/models/%s:generateContent",
projectId, location, model
);
// 构建请求体
JSONObject requestBody = buildRequestBody(prompt, base64Image);
// 获取访问令牌
String accessToken = getGoogleAccessToken();
// 发送HTTP请求
String response = sendHttpRequest(endpoint, requestBody.toString(), accessToken);
// 解析响应并提取图片
return processGoogleAPIResponse(response);
} catch (Exception e) {
@@ -301,66 +316,66 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
throw new BusinessException("Google API call failed", "Google API调用失败", ResultEnum.ERROR.getCode());
}
}
/**
* 构建谷歌API请求体
*/
private JSONObject buildRequestBody(String prompt, String base64Image) {
JSONObject requestBody = new JSONObject();
// 创建图片部分
JSONObject imagePart = new JSONObject();
JSONObject inlineData = new JSONObject();
inlineData.set("mimeType", "image/png");
inlineData.set("data", base64Image.replace("data:image/png;base64,", ""));
imagePart.set("inlineData", inlineData);
// 创建文本部分
JSONObject textPart = new JSONObject();
textPart.set("text", prompt);
// 创建内容对象
JSONObject content = new JSONObject();
content.set("role", "user");
content.set("parts", Arrays.asList(imagePart, textPart));
// 设置contents数组
requestBody.set("contents", Arrays.asList(content));
// 设置生成配置
JSONObject generationConfig = new JSONObject();
generationConfig.set("maxOutputTokens", 8192);
generationConfig.set("responseModalities", Arrays.asList("IMAGE"));
JSONObject imageConfig = new JSONObject();
imageConfig.set("aspectRatio", "9:16");
generationConfig.set("imageConfig", imageConfig);
requestBody.set("generationConfig", generationConfig);
return requestBody;
}
/**
* 获取谷歌访问令牌
*/
private String getGoogleAccessToken() throws IOException {
try (InputStream inputStream = TryOnEffectServiceImpl.class.getClassLoader()
.getResourceAsStream("aida-461108-b4afaabebb84.json")) {
if (inputStream == null) {
throw new IOException("Google credentials file not found");
}
GoogleCredentials credentials = GoogleCredentials
.fromStream(inputStream)
.createScoped(Collections.singletonList("https://www.googleapis.com/auth/cloud-platform"));
credentials.refreshIfExpired();
return credentials.getAccessToken().getTokenValue();
}
}
/**
* 发送HTTP请求
*/
@@ -374,7 +389,7 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
.connectionPool(connectionPool)
.retryOnConnectionFailure(true)
.build();
Request request = new Request.Builder()
.url(endpoint)
.addHeader("Authorization", "Bearer " + accessToken)
@@ -383,15 +398,15 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
.addHeader("Accept", "application/json")
.post(RequestBody.create(MediaType.parse("application/json"), jsonBody))
.build();
// 实现重试逻辑
int maxRetries = 3;
int retryDelay = 2000; // 2秒
for (int attempt = 1; attempt <= maxRetries; attempt++) {
try {
log.info("发起HTTP请求 - 尝试次数: {}/{}, URL: {}", attempt, maxRetries, request.url());
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
log.warn("Google API响应失败状态码: {}", response.code());
@@ -402,7 +417,7 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
throw new IOException("HTTP error code: " + response.code());
}
}
String result = response.body().string();
log.info("Google API调用成功");
return result;
@@ -424,26 +439,26 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
throw new RuntimeException("请求被中断", e);
}
}
throw new IOException("所有重试都失败了");
}
/**
* 处理谷歌API响应
*/
private String processGoogleAPIResponse(String response) {
try {
com.alibaba.fastjson.JSONObject jsonResponse = JSON.parseObject(response);
JSONArray candidates = jsonResponse.getJSONArray("candidates");
if (candidates == null || candidates.isEmpty()) {
log.error("Google API响应中没有候选结果");
throw new BusinessException("Google API response invalid", "Google API响应无效", ResultEnum.ERROR.getCode());
}
com.alibaba.fastjson.JSONObject candidate = candidates.getJSONObject(0);
String finishReason = candidate.getString("finishReason");
if (!"STOP".equals(finishReason)) {
String finishMessage = candidate.getString("finishMessage");
if ("IMAGE_SAFETY".equals(finishReason)) {
@@ -453,32 +468,31 @@ public class TryOnEffectServiceImpl extends ServiceImpl<TryOnEffectMapper, TryOn
log.error("生成失败: {}", finishMessage);
throw new BusinessException("Image generation failed", "图片生成失败", ResultEnum.ERROR.getCode());
}
com.alibaba.fastjson.JSONObject contentResult = candidate.getJSONObject("content");
JSONArray parts = contentResult.getJSONArray("parts");
// 查找包含图片数据的部分
for (int i = 0; i < parts.size(); i++) {
com.alibaba.fastjson.JSONObject part = parts.getJSONObject(i);
if (part.containsKey("inlineData")) {
com.alibaba.fastjson.JSONObject inlineDataResult = part.getJSONObject("inlineData");
String base64Data = inlineDataResult.getString("data");
if (base64Data != null && !base64Data.isEmpty()) {
// 上传生成的图片到MinIO
String fileName = "tryon_result_" + UUID.randomUUID().toString() + ".png";
String uploadedUrl = minioUtil.uploadBytes(
String logicalUrl = minioUtil.uploadBytes(
java.util.Base64.getDecoder().decode(base64Data),
fileName,
MinioFileConstants.FileType.TRY_ON_RESULT,
"image/png"
);
log.info("生成的图片已上传到MinIO: {}", uploadedUrl);
return uploadedUrl;
log.info("生成的图片已上传到MinIO逻辑URL: {}", logicalUrl);
return logicalUrl;
}
}
}
log.error("响应中没有找到有效的图片数据");
throw new BusinessException("Image data not found", "未找到图片数据", ResultEnum.ERROR.getCode());
} catch (BusinessException e) {

View File

@@ -73,8 +73,8 @@ public class VisitRecordServiceImpl extends ServiceImpl<VisitRecordMapper, Visit
LibraryVo libraryVo = new LibraryVo();
// 如果有收藏的试穿效果设置其图片URL为默认图片
if (favoriteEffect != null && favoriteEffect.getResultImageUrl() != null) {
libraryVo.setDefaultImageUrl(minioUtil.getPresignedUrl(
favoriteEffect.getResultImageUrl(),
libraryVo.setDefaultImageUrl(minioUtil.convertToPresignedUrl(
favoriteEffect.getResultImageUrl(),
CommonConstant.MINIO_IMAGE_EXPIRE_TIME
));
} else {

View File

@@ -1,5 +1,6 @@
package com.aida.lanecarford.util;
import com.aida.lanecarford.constant.MinioFileConstants;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@@ -263,11 +264,9 @@ public class ImageCompositionUtil {
* 生成合成图片的文件名
*
* @param originalUrls 原始图片URL列表
* @return 文件名
* @return 生成的文件名
*/
public String generateComposedFileName(List<String> originalUrls) {
String timestamp = String.valueOf(System.currentTimeMillis());
String suffix = originalUrls.size() + "_images_composed";
return String.format("composed_%s_%s.jpg", suffix, timestamp);
return MinioFileConstants.generateComposedImageWithTimestampObjectName(originalUrls.size());
}
}

View File

@@ -1,6 +1,7 @@
package com.aida.lanecarford.util;
import com.aida.lanecarford.config.MinioConfig;
import com.aida.lanecarford.constant.MinioFileConstants;
import com.aida.lanecarford.exception.MinioException;
import io.minio.*;
import io.minio.errors.*;
@@ -9,7 +10,6 @@ import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
@@ -22,18 +22,20 @@ import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* MinIO 工具类
* 提供文件上传、下载、删除等操作
*
* MinIO 工具类 - 重构版本
* 统一命名规范和URL管理
* <p>
* 设计原则:
* 1. 统一使用一个桶lanecarford
* 2. 数据库存储逻辑URL不含桶名的相对路径
* 3. 对外提供预签名URL用于访问
*
* @author Aida
* @since 2024-01-01
*/
@@ -43,14 +45,12 @@ import java.util.concurrent.TimeUnit;
public class MinioUtil {
private final MinioClient minioClient;
private final MinioConfig minioConfig;
// ==================== 基础桶操作 ====================
/**
* 检查存储桶是否存在
*
* @param bucketName 存储桶名称
* @return 是否存在
*/
public boolean bucketExists(String bucketName) {
try {
@@ -63,8 +63,6 @@ public class MinioUtil {
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
*/
public void createBucket(String bucketName) {
try {
@@ -80,8 +78,6 @@ public class MinioUtil {
/**
* 获取所有存储桶
*
* @return 存储桶列表
*/
public List<Bucket> getAllBuckets() {
try {
@@ -92,62 +88,39 @@ public class MinioUtil {
}
}
/**
* 删除存储桶
*
* @param bucketName 存储桶名称
*/
public void removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
log.info("删除存储桶成功: {}", bucketName);
} catch (Exception e) {
log.error("删除存储桶失败: {}", e.getMessage(), e);
throw new MinioException("删除存储桶失败", e);
}
}
// ==================== 文件上传操作 ====================
/**
* 上传文件
*
* @param file 文件
* @return 文件访问URL
* 上传MultipartFile文件
*
* @param file 文件
* @param fileType 文件类型
* @return 逻辑URL不含桶名
*/
public String uploadFile(MultipartFile file) {
return uploadFile(file, minioConfig.getBucketName());
}
/**
* 上传文件到指定存储桶
*
* @param file 文件
* @param bucketName 存储桶名称
* @return 文件访问URL
*/
public String uploadFile(MultipartFile file, String bucketName) {
public String uploadFile(MultipartFile file, MinioFileConstants.FileType fileType) {
if (file == null || file.isEmpty()) {
throw new MinioException("文件不能为空");
}
try {
// 确保存储桶存在
createBucket(bucketName);
// 确保默认桶存在
ensureDefaultBucketExists();
// 生成对象名称
String objectName = MinioFileConstants.generateObjectNameByType(fileType);
// 生成文件名
String fileName = generateFileName(file.getOriginalFilename());
// 上传文件
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.bucket(minioConfig.getBucketName())
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
log.info("文件上传成功: {}", fileName);
return getFileUrl(bucketName, fileName);
log.info("文件上传成功: {}", objectName);
return objectName; // 返回逻辑URL
} catch (Exception e) {
log.error("文件上传失败: {}", e.getMessage(), e);
throw new MinioException("文件上传失败", e);
@@ -156,41 +129,25 @@ public class MinioUtil {
/**
* 上传字节数组
*
*
* @param bytes 字节数组
* @param fileName 文件名
* @param objectName 对象名称(逻辑路径)
* @param contentType 内容类型
* @return 文件访问URL
* @return 逻辑URL不含桶名
*/
public String uploadBytes(byte[] bytes, String fileName, String contentType) {
return uploadBytes(bytes, fileName, contentType, minioConfig.getBucketName());
}
/**
* 上传字节数组到指定存储桶
*
* @param bytes 字节数组
* @param fileName 文件名
* @param contentType 内容类型
* @param bucketName 存储桶名称
* @return 文件访问URL
*/
public String uploadBytes(byte[] bytes, String fileName, String contentType, String bucketName) {
public String uploadBytes(byte[] bytes, String objectName, String contentType) {
if (bytes == null || bytes.length == 0) {
throw new MinioException("文件内容不能为空");
}
try {
// 确保存储桶存在
createBucket(bucketName);
// 生成文件名:TODO 确定好桶的位置后
String objectName = generateFileName(fileName);
// 确保默认桶存在
ensureDefaultBucketExists();
// 上传文件
minioClient.putObject(
PutObjectArgs.builder()
.bucket(bucketName)
.bucket(minioConfig.getBucketName())
.object(objectName)
.stream(new ByteArrayInputStream(bytes), bytes.length, -1)
.contentType(contentType)
@@ -198,7 +155,7 @@ public class MinioUtil {
);
log.info("字节数组上传成功: {}", objectName);
return getFileUrl(bucketName, objectName);
return objectName; // 返回逻辑URL
} catch (Exception e) {
log.error("字节数组上传失败: {}", e.getMessage(), e);
throw new MinioException("字节数组上传失败", e);
@@ -206,36 +163,37 @@ public class MinioUtil {
}
/**
* 下载文件
*
* @param fileName 文件名
* @return 文件输入流
* 上传字节数组(根据文件类型自动生成对象名)
*
* @param bytes 字节数组
* @param fileType 文件类型
* @param contentType 内容类型
* @return 逻辑URL不含桶名
*/
public InputStream downloadFile(String fileName) {
return downloadFile(minioConfig.getBucketName(), fileName);
public String uploadBytes(byte[] bytes, MinioFileConstants.FileType fileType, String contentType) {
String objectName = MinioFileConstants.generateObjectNameByType(fileType);
return uploadBytes(bytes, objectName, contentType);
}
// ==================== 文件下载操作 ====================
/**
* 从指定存储桶下载文件
*
* @param bucketName 存储桶名称
* @param fileName 文件名
* 下载文件
*
* @param objectName 对象名称(逻辑路径)
* @return 文件输入流
*/
public InputStream downloadFile(String bucketName, String fileName) {
public InputStream downloadFile(String objectName) {
try {
// 确保存储桶存在,如果不存在则创建
createBucket(bucketName);
// 检查文件是否存在
if (!doesObjectExist(bucketName, fileName)) {
throw new IOException("File " + fileName + " does not exist in bucket " + bucketName);
if (!doesObjectExist(objectName)) {
throw new IOException("文件不存在: " + objectName);
}
return minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.bucket(minioConfig.getBucketName())
.object(objectName)
.build()
);
} catch (Exception e) {
@@ -244,59 +202,42 @@ public class MinioUtil {
}
}
/**
* 删除文件
*
* @param fileName 文件名
*/
public void deleteFile(String fileName) {
deleteFile(minioConfig.getBucketName(), fileName);
}
// ==================== 文件删除操作 ====================
/**
* 从指定存储桶删除文件
*
* @param bucketName 存储桶名称
* @param fileName 文件名
* 删除文件
*
* @param objectName 对象名称(逻辑路径)
*/
public void deleteFile(String bucketName, String fileName) {
public void deleteFile(String objectName) {
try {
minioClient.removeObject(
RemoveObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.bucket(minioConfig.getBucketName())
.object(objectName)
.build()
);
log.info("文件删除成功: {}", fileName);
log.info("文件删除成功: {}", objectName);
} catch (Exception e) {
log.error("文件删除失败: {}", e.getMessage(), e);
throw new MinioException("文件删除失败", e);
}
}
/**
* 获取文件列表
*
* @param bucketName 存储桶名称
* @return 文件列表
*/
public List<String> listFiles(String bucketName) {
return listFiles(bucketName, null);
}
// ==================== 文件查询操作 ====================
/**
* 获取文件列表
*
* @param bucketName 存储桶名称
* @param prefix 文件前缀
*
* @param prefix 文件前缀
* @return 文件列表
*/
public List<String> listFiles(String bucketName, String prefix) {
public List<String> listFiles(String prefix) {
List<String> files = new ArrayList<>();
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.bucket(minioConfig.getBucketName())
.prefix(prefix)
.build()
);
@@ -314,27 +255,16 @@ public class MinioUtil {
/**
* 检查文件是否存在
*
* @param fileName 文件名
*
* @param objectName 对象名称(逻辑路径)
* @return 是否存在
*/
public boolean fileExists(String fileName) {
return fileExists(minioConfig.getBucketName(), fileName);
}
/**
* 检查文件是否存在
*
* @param bucketName 存储桶名称
* @param fileName 文件名
* @return 是否存在
*/
public boolean fileExists(String bucketName, String fileName) {
public boolean doesObjectExist(String objectName) {
try {
minioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.bucket(minioConfig.getBucketName())
.object(objectName)
.build()
);
return true;
@@ -343,32 +273,25 @@ public class MinioUtil {
}
}
/**
* 获取文件预签名URL用于临时访问
*
* @param fileName 文件名
* @param expires 过期时间(秒)
* @return 预签名URL
*/
public String getPresignedUrl(String fileName, int expires) {
return getPresignedUrl(minioConfig.getBucketName(), fileName, expires);
}
// ==================== URL管理操作 ====================
/**
* 获取文件预签名URL用于临时访问
*
* @param bucketName 存储桶名称
* @param fileName 文件名
* 获取预签名URL用于临时访问
*
* @param objectName 对象名称(逻辑路径)
* @param expires 过期时间(秒)
* @return 预签名URL
*/
public String getPresignedUrl(String bucketName, String fileName, int expires) {
public String getPresignedUrl(String objectName, int expires, String bucketName) {
try {
if (bucketName == null){
bucketName = minioConfig.getBucketName();
}
return minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.object(objectName)
.expiry(expires, TimeUnit.SECONDS)
.build()
);
@@ -379,69 +302,83 @@ public class MinioUtil {
}
/**
* 获取文件访问URL
*
* @param bucketName 存储桶名称
* @param fileName 文件名
* @return 文件访问URL
* 批量获取预签名URL
*
* @param objectNames 对象名称列表(逻辑路径)
* @param expires 过期时间(秒)
* @return 预签名URL列表
*/
public String getFileUrl(String bucketName, String fileName) {
return minioConfig.getEndpoint() + "/" + bucketName + "/" + fileName;
public List<String> getPresignedUrls(List<String> objectNames, int expires) {
List<String> presignedUrls = new ArrayList<>();
for (String objectName : objectNames) {
if (objectName != null && !objectName.trim().isEmpty()) {
presignedUrls.add(getPresignedUrl(objectName, expires, null));
}
}
return presignedUrls;
}
/**
* 生成唯一文件名
*
* @param originalFilename 原始文件名
* @return 生成的文件名
* 判断URL是否为MinIO逻辑URL
*
* @param url URL字符串
* @return 是否为MinIO逻辑URL
*/
private String generateFileName(String originalFilename) {
if (originalFilename == null || originalFilename.trim().isEmpty()) {
throw new MinioException("文件名不能为空");
public boolean isMinioLogicalUrl(String url) {
if (url == null || url.trim().isEmpty()) {
return false;
}
// 获取文件扩展名
String extension = "";
int lastDotIndex = originalFilename.lastIndexOf(".");
if (lastDotIndex > 0) {
extension = originalFilename.substring(lastDotIndex);
}
// 生成唯一文件名
String uuid = UUID.randomUUID().toString().replace("-", "");
return uuid + extension;
// 逻辑URL不包含协议和域名只是相对路径
return !url.startsWith("http://") && !url.startsWith("https://") &&
(url.contains("/") || url.endsWith(".jpg") || url.endsWith(".png") || url.endsWith(".jpeg"));
}
/**
* 将MinIO逻辑URL转换为预签名URL
*
* @param logicalUrl 逻辑URL
* @param expires 过期时间(秒)
* @return 预签名URL如果不是逻辑URL则返回原URL
*/
public String convertToPresignedUrl(String logicalUrl, int expires) {
if (isMinioLogicalUrl(logicalUrl)) {
return getPresignedUrl(logicalUrl, expires, null);
}
return logicalUrl;
}
/**
* 批量转换逻辑URL为预签名URL
*
* @param logicalUrls 逻辑URL列表
* @param expires 过期时间(秒)
* @return 预签名URL列表
*/
public List<String> convertToPresignedUrls(List<String> logicalUrls, int expires) {
List<String> presignedUrls = new ArrayList<>();
for (String logicalUrl : logicalUrls) {
presignedUrls.add(convertToPresignedUrl(logicalUrl, expires));
}
return presignedUrls;
}
// ==================== 图片处理操作 ====================
/**
* 获取压缩后的图片Base64编码
* @param path 图片的minio路径
* @param targetWidth 目标宽度
*
* @param objectName 对象名称(逻辑路径)
* @param targetWidth 目标宽度
* @param targetHeight 目标高度
* @return 压缩后的图片Base64编码
* @throws IOException
*/
public String getCompressedImageAsBase64(String path, int targetWidth, int targetHeight) throws IOException {
int index = path.indexOf("/");
String bucketName = path.substring(0, index);
String fileName = path.substring(index + 1);
// 检查桶是否存在
boolean found = doesObjectExist(bucketName, fileName);
if (!found) {
throw new IOException("Bucket " + bucketName + " does not exist");
}
try (InputStream stream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.build())) {
public String getCompressedImageAsBase64(String objectName, int targetWidth, int targetHeight) throws IOException {
try (InputStream stream = downloadFile(objectName)) {
// 读取原始图片
BufferedImage originalImage = ImageIO.read(stream);
if (originalImage == null) {
throw new IOException("无法读取图片: " + path);
throw new IOException("无法读取图片: " + objectName);
}
// 计算压缩比例,保持宽高比
@@ -470,42 +407,57 @@ public class MinioUtil {
// 转换为Base64
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(compressedImage, "JPEG", baos); // 使用JPEG格式以减小文件大小
ImageIO.write(compressedImage, "JPEG", baos);
byte[] imageBytes = baos.toByteArray();
log.info("图片压缩完成: {} -> {}x{} (原始: {}x{})", path, newWidth, newHeight, originalWidth, originalHeight);
log.info("图片压缩完成: {} -> {}x{} (原始: {}x{})", objectName, newWidth, newHeight, originalWidth, originalHeight);
return Base64.getEncoder().encodeToString(imageBytes);
} catch (ServerException e) {
throw new RuntimeException(e);
} catch (InsufficientDataException e) {
throw new RuntimeException(e);
} catch (ErrorResponseException e) {
throw new RuntimeException(e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
} catch (InvalidResponseException e) {
throw new RuntimeException(e);
} catch (XmlParserException e) {
throw new RuntimeException(e);
} catch (InternalException e) {
throw new RuntimeException(e);
} catch (Exception e) {
log.error("图片压缩失败: {}", e.getMessage(), e);
throw new IOException("图片压缩失败", e);
}
}
public boolean doesObjectExist(String bucketName, String objectName) {
// ==================== 私有辅助方法 ====================
/**
* 确保默认桶存在
*/
private void ensureDefaultBucketExists() {
createBucket(minioConfig.getBucketName());
}
/**
* @deprecated 使用 uploadBytes(byte[], String, String) 替代
*/
@Deprecated
public String uploadBytes(byte[] bytes, String fileName, String contentType, String bucketName) {
log.warn("使用了已废弃的方法建议使用新的API");
if (!bucketName.equals(minioConfig.getBucketName())) {
log.warn("指定的桶名 {} 与配置的桶名 {} 不一致,将使用配置的桶名", bucketName, minioConfig.getBucketName());
}
return uploadBytes(bytes, fileName, contentType);
}
/**
* @deprecated 使用 getPresignedUrl(String, int) 替代
*/
@Deprecated
public String getFileUrl(String bucketName, String fileName) {
log.warn("使用了已废弃的方法 getFileUrl建议使用 getPresignedUrl");
return minioConfig.getEndpoint() + "/" + bucketName + "/" + fileName;
}
/**
* 获取文件字节数组
*/
private byte[] getFileBytes(MultipartFile file) {
try {
minioClient.statObject(
StatObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build()
);
return true;
} catch (Exception e) {
// 如果发生异常,说明文件不存在或者出现了其他错误
return false;
return file.getBytes();
} catch (IOException e) {
throw new MinioException("读取文件字节失败", e);
}
}
}