摘要:AI 高速发展赋能传统业务,图库网站亦有诸多AI 应用空间。以 AI 扩图功能 为例,让我们来学习如何在项目中快速接入 AI 绘图大模型。用户可以选择一张已上传的图片,通过 AI 扩图得到新的图片,希望可以帮到大家。


AI绘图大模型****两次扩图效果:
本节思维导图:
方案设计
1、AI 绘图大模型选择
在 控制台 也能看到对应的图像画面扩展模型

2、调用方式
点击对应的API参考,我们发现,API 只支持异步方式调用

这是因为 AI 绘画任务计算量大且耗时长,同步调用会导致服务器线程长时间被单个任务占用 ,限制了并发处理能力,增加了超时和系统崩溃的风险。通过异步调用,服务器可以将任务放入队列中 ,合理调度资源,避免阻塞主线程,从而更高效地服务多个用户请求,提升整体系统的稳定性和可扩展性。
同步调用如下,好处是客户端可以直接获取到结果,调用更方便:

异步调用如下,客户端需要在提交任务后,不断轮询请求,来检查任务是否执行完成:
由于 AI 接口已经选择了异步调用,所以我们作为要调用 AI 接口的客户端,要使用轮询的方式来检查任务状态是否为 "已完成",如果完成了,才可以获取到生成的图片。
那么是前端轮询还是后端轮询呢?让我们来对比一下
1)前端轮询
前端调用后端提交任务后得到任务 ID,然后通过定时器轮询请求查询任务状态接口,直到任务完成或失败。
TypeScript
// 提交任务
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 秒轮询
}
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");
}
}
}
}
显然,后端轮询容易因为任务阻塞导致资源耗尽,所以通常推荐 前端轮询。除非有明确的需求要求时,才考虑后端轮询,比如任务结果需实时返回且对网络请求数敏感。
在配置文件中填写获取到的 apiKey:
bash
# 阿里云 AI 配置
aliYunAi:
apiKey: xxxx
新建数据模型类
在 api
包下新建 aliyunai
包,存放阿里云 AI 相关代码。
在 aliyunai.model
包下新建数据模型类,可以让 AI 根据官方文档中的请求响应信息自动生成。
AI生成代码小技巧1:
第一步,将请求信息交给AI整理
bash
创建任务获取任务ID
请求方式:POST
URL:https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/out-painting
cURL示例:
bash
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":{
"x_scale":2,
"y_scale":2,
"best_quality":false,
"limit_image_size":true
}
}'
请求头(Headers):
参数 类型 必选 说明
Content-Type string 是 必须设置为 application/json
Authorization string 是 身份认证(示例:Bearer d1xxx2a)
X-DashScope-Async string 是 必须设置为 enable
请求体(Request Body)
顶层参数:
字段 类型 必选 说明 示例值
model string 是 模型名称 image-out-painting
input object 是 输入图像信息
parameters object 是 图像处理参数
input对象属性:
字段 类型 必选 说明 限制条件
image_url string 是 图像URL或base64数据 格式:JPG/JPEG/PNG/HEIF/WEBP
大小:≤10MB
分辨率:512×512 ~ 4096×4096像素
单边长度:[512, 4096]像素
parameters对象属性:
字段 类型 必选 说明 默认值 取值范围/可选值 示例
angle integer 否 逆时针旋转角度(度) 0 [0, 359] -
output_ratio string 否 输出图像宽高比 "" ["", "1:1", "3:4", "4:3", "9:16", "16:9"] -
x_scale float 否 水平方向扩展比例 1.0 [1.0, 3.0] 输入1000×1000 → 设置2.0 → 输出2000×1000
y_scale float 否 垂直方向扩展比例 1.0 [1.0, 3.0] 输入1000×1000 → 设置2.0 → 输出1000×2000
top_offset integer 否 图像上方添加像素 0 top_offset + bottom_offset < 3×原图高度 输入1000×1000 → 设置500 → 输出1000×1500
bottom_offset integer 否 图像下方添加像素 0 top_offset + bottom_offset < 3×原图高度 输入1000×1000 → 设置500 → 输出1000×1500
left_offset integer 否 图像左侧添加像素 0 left_offset + right_offset < 3×原图宽度 输入1000×1000 → 设置500 → 输出1500×1000
right_offset integer 否 图像右侧添加像素 0 left_offset + right_offset < 3×原图宽度 输入1000×1000 → 设置500 → 输出1500×1000
best_quality boolean 否 开启最佳质量模式 false true/false true:耗时增加但细节更丰富
limit_image_size boolean 否 限制输出图像大小 true true/false 建议保持true(输出≤5MB)
add_watermark boolean 否 添加AI水印 true true/false 在左下角添加"Generated by 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;
}
}
然后重复操作,生成响应类即可
创建扩图任务请求类:
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;
}
}
创建扩图任务响应类:
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;
}
查询任务响应类:
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 调用类,通过 Hutool 的 HTTP 请求工具类来调用阿里云百炼的 API:
AI生成代码小技巧2:
第一步,把发送请求样例传递给AI

第二步,让AI利用Hutool包发送请求生成代码
java
@Component
public class AliyunAiApi {
@Value("${aliyunAi.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";
public CreateOutPaintingTaskResponse createPaintingTask(CreateOutPaintingTaskRequest request) {
//1.异常判断
ThrowUtils.throwIf(request == null, ErrorCode.PARAMS_ERROR);
// 2. 构建JSON请求体
String jsonBody = JSONUtil.toJsonStr(request);
// 3. 发送HTTP请求
try (HttpResponse response = HttpRequest.post(CREATE_OUT_PAINTING_TASK_URL)
.header("Authorization", "Bearer " + apiKey)
.header("X-DashScope-Async", "enable")
.header("Content-Type", "application/json")
.body(jsonBody)
.execute()) {
// 4. 检查响应状态
if (!response.isOk()) {
throw new RuntimeException("请求失败,状态码: " + response.getStatus());
}
// 5. 解析响应体
String body = response.body();
return JSONUtil.toBean(body, CreateOutPaintingTaskResponse.class);
} catch (Exception e) {
throw new RuntimeException("创建扩图任务失败", e);
}
}
/**
* 查询创建的任务
*
* @param taskId
* @return
*/
public GetOutPaintingTaskResponse getOutPaintingTask(String taskId) {
if (StrUtil.isBlank(taskId)) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "任务 id 不能为空");
}
try (HttpResponse httpResponse = HttpRequest.get(String.format(GET_OUT_PAINTING_TASK_URL, taskId))
.header(Header.AUTHORIZATION, "Bearer " + apiKey)
.execute()) {
if (!httpResponse.isOk()) {
throw new BusinessException(ErrorCode.OPERATION_ERROR, "获取任务失败");
}
return JSONUtil.toBean(httpResponse.body(), GetOutPaintingTaskResponse.class);
}
}
}
2、扩图服务
在 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;
}
在图片服务中编写创建扩图任务方法,从数据库中获取图片信息和 url 地址,构造请求参数后调用 api 创建扩图任务。注意,如果图片有空间 id,则需要校验权限,直接复用以前的权限校验方法。
java
@Override
public CreateOutPaintingTaskResponse createPictureOutPaintingTask(CreatePictureOutPaintingTaskRequest createPictureOutPaintingTaskRequest, User loginUser) {
// 获取图片信息
Long pictureId = createPictureOutPaintingTaskRequest.getPictureId();
Picture picture = Optional.ofNullable(this.getById(pictureId))
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_ERROR));
// 权限校验
checkPictureAuth(loginUser, picture);
// 构造请求参数
CreateOutPaintingTaskRequest taskRequest = new CreateOutPaintingTaskRequest();
CreateOutPaintingTaskRequest.Input input = new CreateOutPaintingTaskRequest.Input();
input.setImageUrl(picture.getUrl());
taskRequest.setInput(input);
BeanUtil.copyProperties(createPictureOutPaintingTaskRequest, taskRequest);
// 创建任务
return aliYunAiApi.createOutPaintingTask(taskRequest);
}
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);
}
/**
* 查询 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);
}
使用Swagger接口进行测试,成功!