【智能协同云图库】智能协同云图库第八弹:基于阿里云百炼大模型—实现 AI 扩图功能


AI扩图功能****


需求分析


随着 AI 的高速发展,AI 几乎可以应用到任何传统业务中,增强应用的功能,带给用户更好的体验。

对于图库网站来说,AI 也有非常多的应用空间,比如可以利用 AI 绘图大模型来编辑图片,实现扩图、擦除补全、图配文、去水印等功能。

以 AI 扩图功能为例,用户可以选择一张已上传的图片,通过 AI 编辑得到新的图片,并根据情况自行选择是否保存。

该功能不限制仅在空间内使用,公共图库也可以支持。


方案设计


1. AI绘图大模型选择****


AI绘图大模型我们自己是搞不来的,可以选择一个市面上支持AI绘图的大模型。

选择 AI 大模型时,我们最关注的应该是生成效果、生成速度还有价格了吧?当然,对我们学习来说,最关注的还是价格,毕竟绘画大模型的费用不低。

国内的 AI 绘图大模型比较推荐阿里云百炼,它是一站式的大模型开发及应用构建平台,可以通过简单的界面操作,在5分钟内开发出一款大模型应用,并在线体验效果。


创建好应用后,利用官方提供的 API 或SDK,直接通过几行代码,就能在项目中使用大模型应用:


通过阅读 官方文档,发现它是支持AI图像编辑与生成功能的,包括 AI 扩图,支持 HTTP 调用,符合我们的需求。


控制台也能看到对应的图像画面扩展模型:


百炼的大模型提供了新人免费额度,可以通过文档或者点进大模型了解,对于学习用来说足够了:


经过测试,图片生成效果、生成速度都是不错的,因此,本项目将选用阿里云百炼实现AI扩图功能。


建议:之前没接触过类似 AI 大模型平台的同学,先多利用网页控制台熟悉 AI 大模型的 Prompt,了解不同大模型的区别。

推荐学习网站:WaytoAGI-通往AGI之路,最好的 AI 知识库和工具站


2. 调用方式


通过阅读 AI 图像扩展的官方文档,我们发现,API 只支持异步方式调用。


这是因为 AI 绘画任务计算量大且耗时长,同步调用会导致服务器线程长时间被单个任务占用,限制了并发处理能力,增加了超时和系统崩溃的风险。

通过异步调用,服务器可以将任务放入队列中,合理调度资源,避免阻塞主线程,从而更高效地服务多个用户请求,提升整体系统的稳定性和可扩展性。

特点:客户端可以直接获取到结果,调用更方便。


异步调用流程如下,客户端需要在提交任务后,不断轮询请求,来检查任务是否执行完成。

由于 AI 接口已经选择了异步调用,所以我们作为要调用 AI 接口的客户端,要使用轮询的方式来检查任务状态是否为"已完成",如果完成了,才可以获取到生成的图片。

那么是前端轮询还是后端轮询呢?


前端轮询

  • 流程:前端调用后端提交任务后得到任务 ID,然后通过定时器轮询请求查询任务状态接口,直到任务完成或失败。
  • 示例代码:
javascript 复制代码
// 提交任务
async function submitTask() {
  const response = await fetch('/api/createTask', { method: 'POST' });
  const { taskId } = await response.json();
  checkTaskStatus(taskId);
}

// 调用submitTask();

// 检查任务状态
async function checkTaskStatus(taskId) {
  const intervalId = setInterval(async () => {
    const response = await fetch(`/api/taskStatus?taskId=${taskId}`);
    const { status, result } = await response.json();
    if (status === 'success') {
      console.log('Task completed:', result);
      clearInterval(intervalId); // 停止轮询
    } else if (status === 'failed') {
      console.error('Task failed');
      clearInterval(intervalId); // 停止轮询
    }
  }, 2000); // 每隔 2 秒轮询
}

后端轮询

  • 流程:后端通过循环或定时任务检测任务状态,接口保持阻塞,直到任务完成或失败,直接返回结果给前端。
  • 示例代码:
java 复制代码
@RestController
public class TaskController {
    @PostMapping("/createTask")
    public String createTask() {
        String taskId = taskService.submitTask();
        return taskId;
    }

    @GetMapping("/waitForTask")
    public ResponseEntity<String> waitForTask(@RequestParam String taskId) {
        while (true) {
            String status = taskService.checkTaskStatus(taskId);
            if ("success".equals(status)) {
                return ResponseEntity.ok("Task completed");
            } else if ("failed".equals(status)) {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Task failed");
            }
            try {
                Thread.sleep(2000); // 等待 2 秒后重试
            } catch (InterruptedException e) {
                return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Error occurred");
            }
        }
    }
}
  • 后端轮询问题:后端轮询容易因为任务阻塞导致资源耗尽。
  • 推荐方案:通常推荐前端轮询。除非有明确的需求要求时,才考虑后端轮询,比如任务结果需实时返回且对网络请求数敏感。(或者学习时不想写前端的同学哈哈)
  • 选择:此处我们也选择前端轮询方案实现。

💡 小贴士:从这个方案设计中,我们也能感受到,如果你同时了解前端和后端,可以结合二者设计出更合理的方案,而不是把所有的"重担"都交给前端或者后端一方。所以企业中开需求评审会或者讨论方案时,前后端需要紧密协作。


后端开发


1. AI扩图API


(1) 创建API Key****

首先开发业务依赖的基础能力,也就是 AI 扩图 API。

1. 需要先进入阿里云百炼控制台开通服务:


2. 开通推理能力:


3. 开通之后,我们要在控制台获取API Key,可参考文档:

开通之后,在控制台获取 API Key,可参考文档。

能够在控制台查看到 API Key,注意,API Key 一定不要对外泄露!


接下来,我们需要根据下面的请求头使用 Java 来构造扩图请求实体类数据模型


通过阅读文档发现,百炼支持通过 SDK 或 HTTP 调用。

虽然官方写的支持 Java SDK,但 AI 扩图功能中对 SDK 的介绍非常少,此处考虑到兼容性,我们还是使用 HTTP 调用。


4. 由于使用异步的方式,需要开发创建任务和查询结果 2 个 API。


5. 填写配置文件:在配置文件中填写获取到的 apiKey

yaml 复制代码
# 阿里云 AI 配置
aliYunAi:
  apiKey: xxxx

(2) 创建请求参数接收类

api 包下新建 aliyunai 包,存放阿里云 AI 相关代码。


aliyunai.model 包下新建数据模型类,可以让 AI 根据官方文档中的请求响应信息,自动生成请求实体类,无需自己手动编写。

复制下面的请求体内容,交给 AI 生成:


code is cheap, show me the talk !


由于每个 AI 图片处理操作的请求响应都有一些区别,所以单独给 AI 扩图功能编写具体的请求响应类。创建扩图任务请求类:

java 复制代码
@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")
        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;
    }
}

注意:上述代码中,某些字段打上了 Hutool 工具类的 @Alias 注解。这个注解仅对 Hutool 的 JSON 转换生效,对 SpringMVC 的 JSON 转换没有任何影响。

  • 这里有一个很坑的地方,经过测试发现,前端如果传递参数名 xScale,是无法赋值给 xScale 字段的;但是传递参数名 xscale,就可以赋值。

  • 这是因为 SpringMVC 对于第二个字母是大写的参数无法映射(和参数类别无关)。参考博客


解决方案:给这些字段增加 @JsonProperty 注解。

java 复制代码
/**
 * 可选,图像居中,在水平方向上按比例扩展,默认值 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;

为什么 SpringMVC 要这样设计,通过查阅了解到,这是因为 Jackson 在处理字段名与 JSON 属性名映射时,会依赖 Java 的标准命名规范反射 API

  • 举个例子,根据 JavaBean 的规范,属性名称与其访问器方法(getter 和 setter)之间的映射规则是:如果属性名以小写字母开头,第二个字母是大写(如 eMail),规范仍认为属性名称是 eMail,而访问器方法应为 geteMail()seteMail()
  • 但 Jackson 会尝试推断属性名为 email(因为 eMail 不常见),从而导致 JSON 中 eMailemail 可能无法正确映射。

记住结论即可:SpringMVC 默认的序列化器 Jackson 在字段名的第二个字母为大写时,无法正确映射;需要使用 @JsonProperty("yScale") 这样的注解正确映射


(3) 创建扩图任务响应类

这个类同理,不要自己写,直接使用 AI 生成:

java 复制代码
@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;
}

(4) 查询任务响应类


根据官方文档响应的说明,使用 AI 生成对应的查询任务响应类

java 复制代码
@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;
    }
}

(5) 大模型调用API开发

开发 API 调用类,通过 Hutool 的 HTTP 请求工具类来调用阿里云百炼的 API。


注解3:创建任务的请求地址,在官方文档中可以找到


注解4:查询任务状态的请求地址,在官方文档中同样可以找到


注解6:根据官方文档填写请求头


注解7:


注解 11:填写查询任务需要发送的请求头


java 复制代码
@Slf4j
@Component  // 1. 这个类需要读取配置文件中的 APIKey
public class AliYunAiApi {

    // 2. 使用 @Value 注解 (必须是 Spring 包), 读取需要的配置文件
    @Value("${aliYunAi.apiKey}")
    private String apiKey;

    // 3. 创建任务地址
    public static final String CREATE_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/out-painting";

    // 4. 查询任务状态  %s 用于替换实际任务的 {task_id}
    public static final String GET_OUT_PAINTING_TASK_URL = "https://dashscope.aliyuncs.com/api/v1/tasks/%s";

    // 5. 创建任务
    public CreateOutPaintingTaskResponse createOutPaintingTask(CreateOutPaintingTaskRequest createOutPaintingTaskRequest){
        if(createOutPaintingTaskRequest == null){
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "扩图参数为空");
        }

//        curl --location --request POST 'https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/out-painting' \
//        --header "Authorization: Bearer $DASHSCOPE_API_KEY" \
//        --header 'X-DashScope-Async: enable' \
//        --header 'Content-Type: application/json' \
//        --data '{
//        "model": "image-out-painting",
//                "input": {
//            "image_url": "http://xxx/image.jpg"
//        },
//        "parameters":{
//            "angle": 45,
//                    "x_scale":1.5,
//                    "y_scale":1.5
//        }
//    }'

        // 6. 复制上述请求, 然后构造 HTTP 请求, 可以用 AI 生成

        HttpRequest httpRequest = HttpRequest.post(CREATE_OUT_PAINTING_TASK_URL)  // 注解 3 的创建请求地址
                .header("Authorization", "Bearer" + apiKey)  // 填充自定义 APIKey
                .header("X-DashScope-Async", "enable") // 让用户必需显示开启异步, 也方便后续扩展
                .header("Content-Type", "application/json")
                .body(JSONUtil.toJsonStr(createOutPaintingTaskRequest));// 使用 Hutool 的 JSONUtil, 因为刚刚的请求使用了 @Alias

        // 7. 使用 try...with 方法释放 httpRequest 的资源, 自动释放资源的对象必须实现 AutoCloseable 接口
        try(HttpResponse httpResponse = httpRequest.execute()){
            // 8. 响应码异常
            if(!httpResponse.isOk()){
                log.error("请求异常: {}", httpResponse.body());
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败");
            }

            // 9. 将正常的响应体转为 JSON 格式的创建请求的响应对象
            CreateOutPaintingTaskResponse createOutPaintingTaskResponse = JSONUtil.toBean(httpResponse.body(), CreateOutPaintingTaskResponse.class);

            // 10. 拿到响应对象后, 根据响应对象 code 是否有值, 进一步判断扩图是否成功
            if (createOutPaintingTaskResponse.getCode() != null){
                String errMessage = createOutPaintingTaskResponse.getMessage();
                log.error("请求异常: {}", errMessage);
                throw new BusinessException(ErrorCode.OPERATION_ERROR, "AI 扩图失败" + errMessage);
            }

            return createOutPaintingTaskResponse;
        }
    }

    /**
     * 查询创建的任务
     *
     * @param taskId 任务 ID
     * @return 查询任务响应
     */
    public GetOutPaintingTaskResponse getOutPaintingTask(String taskId){
        if(StrUtil.isBlank(taskId)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "任务 ID 不能为空");
        }

        // 11. 填写请求头, 发送请求
//        --header "Authorization: Bearer $DASHSCOPE_API_KEY" \
//        https://dashscope.aliyuncs.com/api/v1/tasks/86ecf553-d340-4e21-xxxxxxxxx

        String url = String.format(GET_OUT_PAINTING_TASK_URL , taskId); // 注解 4 查询请求的 URL , "%s" 替换为 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);
        }
    }
}

注意:要按照官方文档的要求给请求头增加鉴权信息,拼接配置中写好的 apiKey


2. 开发扩图API** 调用接口**


(1) 数据模型开发

model.dto.picture 包下新建 AI 扩图请求类,用于接受前端传来的参数并传递给 Service 服务层。


字段包括图片 id 和扩图参数:

java 复制代码
@Data
public class CreatePictureOutPaintingTaskRequest implements Serializable {
    /**
     * 图片 id
     */
    private Long pictureId;

    /**
     * 扩图参数
     */
    private CreateOutPaintingTaskRequest.Parameters parameters;

    private static final long serialVersionUID = 1L;
}

我们只需要传一个已有的图片,即可实现扩图功能,具体流程:

  1. 前端构造 Parameters(内部类) 的各个参数,并与图片 ID 一起构造扩图请求;
  2. 前端向后端发送扩图请求
  3. 后端从请求中解析图片 ID图像处理参数 Parameters
  4. 查询数据库,找到图片 ID 对应的图片,并进行关于图片与空间的鉴权
  5. 图片的 URLParameters 作为参数,构造API 扩图请求,调用扩图 API;
  6. 扩图 API 解析请求,校验参数,创建扩图任务;
  7. 创建的扩图任务,放入大模型扩图任务队列中,并生成对应的 taskId 返回;
  8. 可以通过调用查看进度 API ,查看对应 taskId 对应的生成进度
  9. 扩图成功后,查看进度 API 会封装URL 到响应中,返回给前端;

(2) 扩图服务开发

在图片服务中编写创建扩图任务方法,从数据库中获取图片信息和 URL 地址,构造请求参数后调用 API 创建扩图任务

注意,如果图片有空间 id,则需要校验权限,直接复用以前的权限校验方法。

java 复制代码
@Override
public void checkPictureAuth(User loginUser, Picture picture) {
    Long spaceId = picture.getSpaceId();
    Long loginUserId = loginUser.getId();
    if (spaceId == null) {
        // 公共图库, 仅本人和管理员可操作
        if (!picture.getUserId().equals(loginUserId) && !userService.isAdmin(loginUser)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
    } else {
        // 私有空间, 仅空间管理员可操作
        if (!picture.getUserId().equals(loginUserId)) {
            throw new BusinessException(ErrorCode.NO_AUTH_ERROR);
        }
    }
}

在调用大模型接口前,先调用该方法,对图片进行鉴权,只有空间管理员,可以对图片调用扩图 API


接下来,我们对调用大模型 API 进行服务开发:


java 复制代码
/**
 * 创建扩图任务
 * @param createPictureOutPaintingTaskRequest  扩图请求
 * @param loginUser
 * @return
 */
CreateOutPaintingTaskResponse createPictureOutPaintingTask(CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest, User loginUser);

java 复制代码
@Resource
private AliYunAiApi aliYunAiApi;

@Override
public CreateOutPaintingTaskResponse createPictureOutPaintingTask(CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest, User loginUser) {
    // 1. 根据请求获取图片 ID
    Long pictureId = createPictureOutPaintingTaskRequest.getPictureId();

    // 2. 查询数据库, 获取图片, 如果数据库没有该图片, 抛异常
//        Picture picture = this.getById(pictureId);
//        ThrowUtils.throwIf(picture == null, ErrorCode.NOT_FOUND_ERROR, "图片不存在");

    Picture picture = Optional.ofNullable(this.getById(pictureId))
            .orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_ERROR, "图片不存在"));
    // Optional.ofNullable(...): 安全地包装可能为 null 的查询结果;
    // orElseThrow(...):如果结果确实为 null, 就立即抛出指定的异常;

    // 3. 校验权限
    checkPictureAuth(loginUser, picture);

    // 4. 创建扩图任务请求
    CreateOutPaintingTaskRequest createOutPaintingTaskRequest = new CreateOutPaintingTaskRequest();
    CreateOutPaintingTaskRequest.Input input = new CreateOutPaintingTaskRequest.Input();
    // 内部类 Input, 也作为参数
    input.setImageUrl(picture.getUrl());
    createOutPaintingTaskRequest.setInput(input);
    createOutPaintingTaskRequest.setParameters(createPictureOutPaintingTaskRequest.getParameters());

    // 5. 调用 API 创建任务
    return aliYunAiApi.createOutPaintingTask(createOutPaintingTaskRequest);
}

(3) 扩图接口开发

PictureController 添加 AI 扩图接口,包括创建任务和查询任务状态接口:

java 复制代码
/**
 * 创建 AI 扩图任务
 */
@PostMapping("/out_painting/create_task")
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);
}

java 复制代码
@Resource
private AliYunAiApi aliYunAiApi;

/**
 * 查询 AI 扩图任务
 */
@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);
}

(4) 接口测试

测试图片:


发送扩图任务请求:


响应:


复制 taskId,调用查看任务接口:


打开 outputImageUrl


效果对比:

至此,我们的 AI 图片编辑后端开发完成啦~~~~


相关推荐
counterxing6 小时前
Agent 跑起来之后,难的是复用、观测和评测
node.js·agent·ai编程
uccs6 小时前
大模型底层机制与Agent开发
agent·ai编程·claude
counterxing6 小时前
我把 Codex 里的 Skills 做成了一个 MCP,还支持分享
前端·agent·ai编程
夜雪闻竹7 小时前
vectra 向量索引文件损坏怎么办
ai编程·向量·vectra
ZzT7 小时前
Harness 到底指什么
openai·ai编程·claude
宅小年7 小时前
AI 创业最危险的地方:太容易做出来
openai·ai编程·claude
麦客奥德彪7 小时前
Android Skills
架构·ai编程
言萧凡_CookieBoty8 小时前
一文讲清 RAG:让 AI 读懂业务知识库的核心方法
ai编程
kyriewen9 小时前
产品经理把PRD写成“天书”,我用AI半小时重写了一遍,他当场愣住
前端·ai编程·cursor
Patrick_Wilson9 小时前
知识沉淀的四层模型:从个人笔记到企业资产,让文档真正长出复利
面试·程序员·ai编程