4.7 阿里云百炼AI扩图接口实现
This commit is contained in:
parent
a44466e846
commit
e40c4a975b
@ -0,0 +1,87 @@
|
|||||||
|
package edu.whut.smilepicturebackend.api.aliyunai;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import cn.hutool.http.HttpRequest;
|
||||||
|
import cn.hutool.http.HttpResponse;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskRequest;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskResponse;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.GetOutPaintingTaskResponse;
|
||||||
|
import edu.whut.smilepicturebackend.exception.BusinessException;
|
||||||
|
import edu.whut.smilepicturebackend.exception.ErrorCode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class AliYunAiApi {
|
||||||
|
|
||||||
|
// 读取配置文件
|
||||||
|
@Value("${smile-picture.aliyun.apiKey}")
|
||||||
|
private String apiKey;
|
||||||
|
|
||||||
|
// 创建任务地址
|
||||||
|
public static final String CREATE_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/out-painting";
|
||||||
|
|
||||||
|
// 查询任务状态
|
||||||
|
public static final String GET_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/tasks/%s";
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建任务
|
||||||
|
*
|
||||||
|
* @param createOutPaintingTaskRequest
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public CreateOutPaintingTaskResponse createOutPaintingTask(CreateOutPaintingTaskRequest createOutPaintingTaskRequest) {
|
||||||
|
if (createOutPaintingTaskRequest == null) {
|
||||||
|
throw new BusinessException(ErrorCode.OPERATION_ERROR, "扩图参数为空");
|
||||||
|
}
|
||||||
|
// 发送请求
|
||||||
|
HttpRequest httpRequest = HttpRequest.post(CREATE_OUT_PAINTING_TASK_URL)
|
||||||
|
.header("Authorization", "Bearer " + apiKey)
|
||||||
|
// 必须开启异步处理 enable
|
||||||
|
.header("X-DashScope-Async", "enable")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.body(JSONUtil.toJsonStr(createOutPaintingTaskRequest));
|
||||||
|
// 处理响应
|
||||||
|
//这段代码会在 try 块结束时(正常返回或抛出异常)自动调用 httpResponse.close(),从而释放所有底层资源
|
||||||
|
try (HttpResponse httpResponse = httpRequest.execute()) {
|
||||||
|
if (!httpResponse.isOk()) {
|
||||||
|
log.error("请求异常:{}", httpResponse.body());
|
||||||
|
throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败");
|
||||||
|
}
|
||||||
|
CreateOutPaintingTaskResponse createOutPaintingTaskResponse = JSONUtil.toBean(httpResponse.body(), CreateOutPaintingTaskResponse.class);
|
||||||
|
if (createOutPaintingTaskResponse.getCode() != null) {
|
||||||
|
String errorMessage = createOutPaintingTaskResponse.getMessage();
|
||||||
|
log.error("请求异常:{}", errorMessage);
|
||||||
|
throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败," + errorMessage);
|
||||||
|
}
|
||||||
|
return createOutPaintingTaskResponse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询创建的任务结果
|
||||||
|
*
|
||||||
|
* @param taskId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public GetOutPaintingTaskResponse getOutPaintingTask(String taskId) {
|
||||||
|
if (StrUtil.isBlank(taskId)) {
|
||||||
|
throw new BusinessException(ErrorCode.OPERATION_ERROR, "任务 ID 不能为空");
|
||||||
|
}
|
||||||
|
// 处理响应
|
||||||
|
String url = String.format(GET_OUT_PAINTING_TASK_URL, taskId);
|
||||||
|
try (HttpResponse httpResponse = HttpRequest.get(url)
|
||||||
|
.header("Authorization", "Bearer " + apiKey)
|
||||||
|
.execute()) {
|
||||||
|
if (!httpResponse.isOk()) {
|
||||||
|
log.error("请求异常:{}", httpResponse.body());
|
||||||
|
throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取任务结果失败");
|
||||||
|
}
|
||||||
|
return JSONUtil.toBean(httpResponse.body(), GetOutPaintingTaskResponse.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,112 @@
|
|||||||
|
package edu.whut.smilepicturebackend.api.aliyunai.model;
|
||||||
|
|
||||||
|
import cn.hutool.core.annotation.Alias;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建扩图任务请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CreateOutPaintingTaskRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型,例如 "image-out-painting"
|
||||||
|
*/
|
||||||
|
private String model = "image-out-painting";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输入图像信息
|
||||||
|
*/
|
||||||
|
private Input input;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图像处理参数
|
||||||
|
*/
|
||||||
|
private Parameters parameters;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Input {
|
||||||
|
/**
|
||||||
|
* 必选,图像 URL
|
||||||
|
*/
|
||||||
|
@Alias("image_url") //java类中一般写驼峰式的,但是大模型接收的是image_url 所以用@Alias
|
||||||
|
private String imageUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Parameters implements Serializable {
|
||||||
|
/**
|
||||||
|
* 可选,逆时针旋转角度,默认值 0,取值范围 [0, 359]
|
||||||
|
*/
|
||||||
|
private Integer angle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,输出图像的宽高比,默认空字符串,不设置宽高比
|
||||||
|
* 可选值:["", "1:1", "3:4", "4:3", "9:16", "16:9"]
|
||||||
|
*/
|
||||||
|
@Alias("output_ratio")
|
||||||
|
private String outputRatio;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,图像居中,在水平方向上按比例扩展,默认值 1.0,范围 [1.0, 3.0]
|
||||||
|
*/
|
||||||
|
@Alias("x_scale")
|
||||||
|
@JsonProperty("xScale")
|
||||||
|
private Float xScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,图像居中,在垂直方向上按比例扩展,默认值 1.0,范围 [1.0, 3.0]
|
||||||
|
*/
|
||||||
|
@Alias("y_scale")
|
||||||
|
@JsonProperty("yScale")
|
||||||
|
private Float yScale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,在图像上方添加像素,默认值 0
|
||||||
|
*/
|
||||||
|
@Alias("top_offset")
|
||||||
|
private Integer topOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,在图像下方添加像素,默认值 0
|
||||||
|
*/
|
||||||
|
@Alias("bottom_offset")
|
||||||
|
private Integer bottomOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,在图像左侧添加像素,默认值 0
|
||||||
|
*/
|
||||||
|
@Alias("left_offset")
|
||||||
|
private Integer leftOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,在图像右侧添加像素,默认值 0
|
||||||
|
*/
|
||||||
|
@Alias("right_offset")
|
||||||
|
private Integer rightOffset;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,开启图像最佳质量模式,默认值 false
|
||||||
|
* 若为 true,耗时会成倍增加
|
||||||
|
*/
|
||||||
|
@Alias("best_quality")
|
||||||
|
private Boolean bestQuality;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,限制模型生成的图像文件大小,默认值 true
|
||||||
|
* - 单边长度 <= 10000:输出图像文件大小限制为 5MB 以下
|
||||||
|
* - 单边长度 > 10000:输出图像文件大小限制为 10MB 以下
|
||||||
|
*/
|
||||||
|
@Alias("limit_image_size")
|
||||||
|
private Boolean limitImageSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 可选,添加 "Generated by AI" 水印,默认值 true
|
||||||
|
*/
|
||||||
|
@Alias("add_watermark")
|
||||||
|
private Boolean addWatermark = false;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
package edu.whut.smilepicturebackend.api.aliyunai.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建扩图任务响应类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class CreateOutPaintingTaskResponse {
|
||||||
|
|
||||||
|
private Output output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示任务的输出信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class Output {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务 ID
|
||||||
|
*/
|
||||||
|
private String taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态
|
||||||
|
* <ul>
|
||||||
|
* <li>PENDING:排队中</li>
|
||||||
|
* <li>RUNNING:处理中</li>
|
||||||
|
* <li>SUSPENDED:挂起</li>
|
||||||
|
* <li>SUCCEEDED:执行成功</li>
|
||||||
|
* <li>FAILED:执行失败</li>
|
||||||
|
* <li>UNKNOWN:任务不存在或状态未知</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private String taskStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口错误码。
|
||||||
|
* <p>接口成功请求不会返回该参数。</p>
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口错误信息。
|
||||||
|
* <p>接口成功请求不会返回该参数。</p>
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求唯一标识。
|
||||||
|
* <p>可用于请求明细溯源和问题排查。</p>
|
||||||
|
*/
|
||||||
|
private String requestId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,111 @@
|
|||||||
|
package edu.whut.smilepicturebackend.api.aliyunai.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询扩图任务响应类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class GetOutPaintingTaskResponse {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 请求唯一标识
|
||||||
|
*/
|
||||||
|
private String requestId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出信息
|
||||||
|
*/
|
||||||
|
private Output output;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示任务的输出信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class Output {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务 ID
|
||||||
|
*/
|
||||||
|
private String taskId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态
|
||||||
|
* <ul>
|
||||||
|
* <li>PENDING:排队中</li>
|
||||||
|
* <li>RUNNING:处理中</li>
|
||||||
|
* <li>SUSPENDED:挂起</li>
|
||||||
|
* <li>SUCCEEDED:执行成功</li>
|
||||||
|
* <li>FAILED:执行失败</li>
|
||||||
|
* <li>UNKNOWN:任务不存在或状态未知</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private String taskStatus;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交时间
|
||||||
|
* 格式:YYYY-MM-DD HH:mm:ss.SSS
|
||||||
|
*/
|
||||||
|
private String submitTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 调度时间
|
||||||
|
* 格式:YYYY-MM-DD HH:mm:ss.SSS
|
||||||
|
*/
|
||||||
|
private String scheduledTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 结束时间
|
||||||
|
* 格式:YYYY-MM-DD HH:mm:ss.SSS
|
||||||
|
*/
|
||||||
|
private String endTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 输出图像的 URL
|
||||||
|
*/
|
||||||
|
private String outputImageUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口错误码
|
||||||
|
* <p>接口成功请求不会返回该参数</p>
|
||||||
|
*/
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 接口错误信息
|
||||||
|
* <p>接口成功请求不会返回该参数</p>
|
||||||
|
*/
|
||||||
|
private String message;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务指标信息
|
||||||
|
*/
|
||||||
|
private TaskMetrics taskMetrics;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表示任务的统计信息
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public static class TaskMetrics {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 总任务数
|
||||||
|
*/
|
||||||
|
private Integer total;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功任务数
|
||||||
|
*/
|
||||||
|
private Integer succeeded;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失败任务数
|
||||||
|
*/
|
||||||
|
private Integer failed;
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,11 @@
|
|||||||
package edu.whut.smilepicturebackend.controller;
|
package edu.whut.smilepicturebackend.controller;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import edu.whut.smilepicturebackend.annotation.AuthCheck;
|
import edu.whut.smilepicturebackend.annotation.AuthCheck;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.AliYunAiApi;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskResponse;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.GetOutPaintingTaskResponse;
|
||||||
import edu.whut.smilepicturebackend.api.imagesearch.ImageSearchApiFacade;
|
import edu.whut.smilepicturebackend.api.imagesearch.ImageSearchApiFacade;
|
||||||
import edu.whut.smilepicturebackend.api.imagesearch.model.ImageSearchResult;
|
import edu.whut.smilepicturebackend.api.imagesearch.model.ImageSearchResult;
|
||||||
import edu.whut.smilepicturebackend.common.BaseResponse;
|
import edu.whut.smilepicturebackend.common.BaseResponse;
|
||||||
@ -24,7 +28,6 @@ import edu.whut.smilepicturebackend.service.UserService;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.BeanUtils;
|
||||||
import org.springframework.util.DigestUtils;
|
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
@ -42,6 +45,7 @@ public class PictureController {
|
|||||||
private final UserService userService;
|
private final UserService userService;
|
||||||
private final PictureService pictureService;
|
private final PictureService pictureService;
|
||||||
private final SpaceService spaceService;
|
private final SpaceService spaceService;
|
||||||
|
private final AliYunAiApi aliYunAiApi;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -305,4 +309,32 @@ public class PictureController {
|
|||||||
pictureService.editPictureByBatch(pictureEditByBatchRequest, loginUser);
|
pictureService.editPictureByBatch(pictureEditByBatchRequest, loginUser);
|
||||||
return ResultUtils.success(true);
|
return ResultUtils.success(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*创建 AI 扩图任务
|
||||||
|
* @param createPictureOutPaintingTaskRequest
|
||||||
|
* @param request
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping("/out_painting/create_task")
|
||||||
|
// @SaSpaceCheckPermission(value = SpaceUserPermissionConstant.PICTURE_EDIT)
|
||||||
|
public BaseResponse<CreateOutPaintingTaskResponse> createPictureOutPaintingTask(@RequestBody CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest,
|
||||||
|
HttpServletRequest request) {
|
||||||
|
if (createPictureOutPaintingTaskRequest == null || createPictureOutPaintingTaskRequest.getPictureId() == null) {
|
||||||
|
throw new BusinessException(ErrorCode.PARAMS_ERROR);
|
||||||
|
}
|
||||||
|
User loginUser = userService.getLoginUser(request);
|
||||||
|
CreateOutPaintingTaskResponse response = pictureService.createPictureOutPaintingTask(createPictureOutPaintingTaskRequest, loginUser);
|
||||||
|
return ResultUtils.success(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询 AI 扩图任务 ,前端轮询结果,当结果中有SUCCESS,那么同时取出该响应中的结果。
|
||||||
|
*/
|
||||||
|
@GetMapping("/out_painting/get_task")
|
||||||
|
public BaseResponse<GetOutPaintingTaskResponse> getPictureOutPaintingTask(String taskId) {
|
||||||
|
ThrowUtils.throwIf(StrUtil.isBlank(taskId), ErrorCode.PARAMS_ERROR);
|
||||||
|
GetOutPaintingTaskResponse task = aliYunAiApi.getOutPaintingTask(taskId);
|
||||||
|
return ResultUtils.success(task);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,6 +58,7 @@ public abstract class PictureUploadTemplate {
|
|||||||
String uploadPath = String.format("/%s/%s/%s",projectName, uploadPathPrefix, uploadFilename);
|
String uploadPath = String.format("/%s/%s/%s",projectName, uploadPathPrefix, uploadFilename);
|
||||||
File file = null;
|
File file = null;
|
||||||
try {
|
try {
|
||||||
|
log.info("uploadPath"+uploadPath);
|
||||||
// 3. 创建临时文件,获取文件到服务器
|
// 3. 创建临时文件,获取文件到服务器
|
||||||
file = File.createTempFile(uploadPath, null);
|
file = File.createTempFile(uploadPath, null);
|
||||||
// 处理文件来源
|
// 处理文件来源
|
||||||
|
@ -9,6 +9,7 @@ import cn.hutool.http.Method;
|
|||||||
import edu.whut.smilepicturebackend.exception.BusinessException;
|
import edu.whut.smilepicturebackend.exception.BusinessException;
|
||||||
import edu.whut.smilepicturebackend.exception.ErrorCode;
|
import edu.whut.smilepicturebackend.exception.ErrorCode;
|
||||||
import edu.whut.smilepicturebackend.exception.ThrowUtils;
|
import edu.whut.smilepicturebackend.exception.ThrowUtils;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -21,6 +22,7 @@ import java.util.List;
|
|||||||
* URL 图片上传
|
* URL 图片上传
|
||||||
*/
|
*/
|
||||||
@Service
|
@Service
|
||||||
|
@Slf4j
|
||||||
public class UrlPictureUpload extends PictureUploadTemplate {
|
public class UrlPictureUpload extends PictureUploadTemplate {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -46,14 +48,15 @@ public class UrlPictureUpload extends PictureUploadTemplate {
|
|||||||
* @return 不含点的扩展名,比如 "jpg","png",取不出时返回空串
|
* @return 不含点的扩展名,比如 "jpg","png",取不出时返回空串
|
||||||
*/
|
*/
|
||||||
protected String fetchAndValidateExtension(String fileUrl) {
|
protected String fetchAndValidateExtension(String fileUrl) {
|
||||||
|
log.info("收到的fileurl:{}",fileUrl);
|
||||||
try (HttpResponse resp = HttpUtil
|
try (HttpResponse resp = HttpUtil
|
||||||
.createRequest(Method.HEAD, fileUrl)
|
.createRequest(Method.HEAD, fileUrl)
|
||||||
.execute()) {
|
.execute()) {
|
||||||
if (resp.getStatus() != HttpStatus.HTTP_OK) {
|
if (resp.getStatus() != HttpStatus.HTTP_OK) {
|
||||||
throw new BusinessException(
|
// 网络或权限问题时直接降级
|
||||||
ErrorCode.OPERATION_ERROR, "文件不存在或不可访问");
|
log.warn("HEAD 请求未返回 200,status = {},将从 URL 中提取后缀", resp.getStatus());
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1) Content-Type 验证 & 提取扩展名
|
// 1) Content-Type 验证 & 提取扩展名
|
||||||
String ct = resp.header("Content-Type");
|
String ct = resp.header("Content-Type");
|
||||||
String ext = "";
|
String ext = "";
|
||||||
@ -95,16 +98,21 @@ public class UrlPictureUpload extends PictureUploadTemplate {
|
|||||||
@Override
|
@Override
|
||||||
protected String getOriginFilename(Object inputSource) {
|
protected String getOriginFilename(Object inputSource) {
|
||||||
String fileUrl = (String) inputSource;
|
String fileUrl = (String) inputSource;
|
||||||
// 1) HEAD 验证并拿扩展名(只这一处会发 HEAD)
|
// 先把 query string 去掉
|
||||||
String ext = fetchAndValidateExtension(fileUrl);
|
int qIdx = fileUrl.indexOf('?');
|
||||||
// 2) fallback:若服务器没返回类型,再从 URL 中简单截取
|
String cleanUrl = qIdx > 0 ? fileUrl.substring(0, qIdx) : fileUrl;
|
||||||
|
|
||||||
|
// 1) 尝试 HEAD 拿扩展名,不可用时 ext == ""
|
||||||
|
String ext = fetchAndValidateExtension(cleanUrl);
|
||||||
|
|
||||||
|
// 2) fallback:若 ext 为空,就直接返回去掉参数后的 URL
|
||||||
if (StrUtil.isBlank(ext)) {
|
if (StrUtil.isBlank(ext)) {
|
||||||
ext = FileUtil.extName(fileUrl);
|
return cleanUrl;
|
||||||
|
} else {
|
||||||
|
// 3) 从 cleanUrl 中拿到 baseName,再拼回去
|
||||||
|
String base = FileUtil.mainName(FileUtil.getName(cleanUrl));
|
||||||
|
return base + "." + ext;
|
||||||
}
|
}
|
||||||
ThrowUtils.throwIf(ext==null,ErrorCode.PARAMS_ERROR,"不正确的图片格式");
|
|
||||||
// 3) 拿到 baseName,然后拼回去
|
|
||||||
String base = FileUtil.mainName(FileUtil.getName(fileUrl));
|
|
||||||
return StrUtil.isNotBlank(ext) ? base + "." + ext : base;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package edu.whut.smilepicturebackend.model.dto.picture;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskRequest;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建扩图任务请求
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class CreatePictureOutPaintingTaskRequest implements Serializable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片 id
|
||||||
|
*/
|
||||||
|
private Long pictureId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 扩图参数
|
||||||
|
*/
|
||||||
|
private CreateOutPaintingTaskRequest.Parameters parameters;
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
}
|
@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.IService;
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskResponse;
|
||||||
import edu.whut.smilepicturebackend.api.imagesearch.model.ImageSearchResult;
|
import edu.whut.smilepicturebackend.api.imagesearch.model.ImageSearchResult;
|
||||||
import edu.whut.smilepicturebackend.model.dto.picture.*;
|
import edu.whut.smilepicturebackend.model.dto.picture.*;
|
||||||
import edu.whut.smilepicturebackend.model.entity.Picture;
|
import edu.whut.smilepicturebackend.model.entity.Picture;
|
||||||
@ -144,4 +145,11 @@ public interface PictureService extends IService<Picture> {
|
|||||||
*/
|
*/
|
||||||
void editPictureByBatch(PictureEditByBatchRequest pictureEditByBatchRequest, User loginUser);
|
void editPictureByBatch(PictureEditByBatchRequest pictureEditByBatchRequest, User loginUser);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建扩图任务
|
||||||
|
*
|
||||||
|
* @param createPictureOutPaintingTaskRequest
|
||||||
|
* @param loginUser
|
||||||
|
*/
|
||||||
|
CreateOutPaintingTaskResponse createPictureOutPaintingTask(CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest, User loginUser);
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,9 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|||||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.AliYunAiApi;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskRequest;
|
||||||
|
import edu.whut.smilepicturebackend.api.aliyunai.model.CreateOutPaintingTaskResponse;
|
||||||
import edu.whut.smilepicturebackend.api.imagesearch.ImageSearchApiFacade;
|
import edu.whut.smilepicturebackend.api.imagesearch.ImageSearchApiFacade;
|
||||||
import edu.whut.smilepicturebackend.api.imagesearch.model.ImageSearchResult;
|
import edu.whut.smilepicturebackend.api.imagesearch.model.ImageSearchResult;
|
||||||
import edu.whut.smilepicturebackend.exception.BusinessException;
|
import edu.whut.smilepicturebackend.exception.BusinessException;
|
||||||
@ -76,6 +79,8 @@ public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
|
|||||||
private final CosManager cosManager;
|
private final CosManager cosManager;
|
||||||
private final SpaceService spaceService;
|
private final SpaceService spaceService;
|
||||||
private final TransactionTemplate transactionTemplate;
|
private final TransactionTemplate transactionTemplate;
|
||||||
|
private final AliYunAiApi aliYunAiApi;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void validPicture(Picture picture) {
|
public void validPicture(Picture picture) {
|
||||||
ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR);
|
ThrowUtils.throwIf(picture == null, ErrorCode.PARAMS_ERROR);
|
||||||
@ -154,6 +159,7 @@ public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
|
|||||||
// 根据 inputSource 的类型区分上传方式!!
|
// 根据 inputSource 的类型区分上传方式!!
|
||||||
PictureUploadTemplate pictureUploadTemplate = filePictureUpload;
|
PictureUploadTemplate pictureUploadTemplate = filePictureUpload;
|
||||||
if (inputSource instanceof String) {
|
if (inputSource instanceof String) {
|
||||||
|
log.info("收到 upload/url 请求,url = {}", inputSource);
|
||||||
pictureUploadTemplate = urlPictureUpload;
|
pictureUploadTemplate = urlPictureUpload;
|
||||||
}
|
}
|
||||||
//上传到腾讯云COS上
|
//上传到腾讯云COS上
|
||||||
@ -680,6 +686,26 @@ public class PictureServiceImpl extends ServiceImpl<PictureMapper, Picture>
|
|||||||
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "批量编辑失败");
|
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR, "批量编辑失败");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CreateOutPaintingTaskResponse createPictureOutPaintingTask(CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest, User loginUser) {
|
||||||
|
// 获取图片信息
|
||||||
|
Long pictureId = createPictureOutPaintingTaskRequest.getPictureId();
|
||||||
|
Picture picture = this.getById(pictureId);
|
||||||
|
if (picture == null) {
|
||||||
|
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在");
|
||||||
|
}
|
||||||
|
// 校验权限,已经改为使用注解鉴权
|
||||||
|
// checkPictureAuth(loginUser, picture);
|
||||||
|
// 创建扩图任务
|
||||||
|
CreateOutPaintingTaskRequest createOutPaintingTaskRequest = new CreateOutPaintingTaskRequest();
|
||||||
|
CreateOutPaintingTaskRequest.Input input = new CreateOutPaintingTaskRequest.Input();
|
||||||
|
input.setImageUrl(picture.getUrl());
|
||||||
|
createOutPaintingTaskRequest.setInput(input);
|
||||||
|
createOutPaintingTaskRequest.setParameters(createPictureOutPaintingTaskRequest.getParameters());
|
||||||
|
// 创建任务
|
||||||
|
return aliYunAiApi.createOutPaintingTask(createOutPaintingTaskRequest);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* nameRule 格式:图片-{序号} =>图片-1 图片-2 ...
|
* nameRule 格式:图片-{序号} =>图片-1 图片-2 ...
|
||||||
*
|
*
|
||||||
|
@ -68,4 +68,8 @@ cos:
|
|||||||
secretId: ${smile-picture.cos.client.secretId}
|
secretId: ${smile-picture.cos.client.secretId}
|
||||||
secretKey: ${smile-picture.cos.client.secretKey}
|
secretKey: ${smile-picture.cos.client.secretKey}
|
||||||
region: ${smile-picture.cos.client.region}
|
region: ${smile-picture.cos.client.region}
|
||||||
bucket: ${smile-picture.cos.client.bucket}
|
bucket: ${smile-picture.cos.client.bucket}
|
||||||
|
|
||||||
|
smile-picture:
|
||||||
|
aliyun:
|
||||||
|
apiKey: ${smile-picture.aliyun.apiKey}
|
Loading…
x
Reference in New Issue
Block a user