摘要:AI 高速發展賦能傳統業務,圖庫網站亦有諸多 AI 應用空間。以 AI 擴圖功?能為例,讓我們來學習如何在項目?中快速接入 AI 繪圖大模型。?用戶可以選擇一張已上傳的圖片,?通過 AI 擴圖得到新的圖片,希望可以幫到大家。
AI繪圖大模型兩次擴圖效果:
本節思維導圖:
方案設計
1、AI 繪圖大模型選擇
在 控制臺 也能看到對應的圖像畫面擴展模型
2、調用方式
點擊對應的API參考,我們發現,API 只支持異步方式調用
這是因為 AI 繪畫任務計?算量大且耗時長,同步調用會導致服務器線程長時間被單個?任務占用,限制了并發處理能力,增加了超時和系統崩潰的?風險。通過異步調用,服務器可以將任務放入隊列中,合理?調度資源,避免阻塞主線程,從而更高效地服務多個用戶請?求,提升整體系統的穩定性和可擴展性。
同步調用如下,好處是客戶?端可以直接獲取到結?果,調用更方便:
異步調用如下,客戶端需要在提交任務后?,不斷輪詢請求,來檢查任務?是否執行完成:
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??????????????????????????
由于 AI 接口?已經選擇了異步調用,所以我們作?為要調用 AI 接口的客戶端,?要使用輪詢的方式來檢查任務狀態?是否為 “已完成”,如果完成了?,才可以獲取到生成的圖片。
那么是前端輪詢還是后端輪詢呢?讓我們來對比一下
1)前端輪詢
前端調用后?端提交任務后得到任?務 ID,然后通過?定時器輪詢請求查詢?任務狀態接口,直到?任務完成或失敗。
// 提交任務
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)后端輪詢
后端通過循?環或定時任務檢測任?務狀態,接口阻?塞,直到任務完成或?失敗,直接返回結?果給前端。
@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:
# 阿里云 AI 配置
aliYunAi:apiKey: xxxx
新建數據模型類
在 api
包下新建 aliyunai
包,存放阿里云 AI 相關代碼。
在 aliyunai.model
包下新建數據模型類,可以讓 AI 根據官方文檔中的請求響應信息自動生成。
AI生成代碼小技巧1:
第一步,將請求信息交給AI整理
創建任務獲取任務ID
請求方式:POST
URL:https://dashscope.aliyuncs.com/api/v1/services/aigc/image2image/out-paintingcURL示例: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生成對應的請求類代碼
@Data
public class CreateOutPaintingTaskRequest implements Serializable {/*** 模型,例如 "image-out-painting"*/private String model = "image-out-painting";/*** 輸入圖像信息*/private Input input;/*** 圖像處理參數*/private Parameters parameters;@Datapublic static class Input {/*** 必選,圖像 URL*/@Alias("image_url")private String imageUrl;}@Datapublic 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;}
}
然后重復操作,生成響應類即可
創建擴圖任務請求類:
@Data
public class CreateOutPaintingTaskRequest implements Serializable {/*** 模型,例如 "image-out-painting"*/private String model = "image-out-painting";/*** 輸入圖像信息*/private Input input;/*** 圖像處理參數*/private Parameters parameters;@Datapublic static class Input {/*** 必選,圖像 URL*/@Alias("image_url")private String imageUrl;}@Datapublic 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;}
}
創建擴圖任務響應類:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CreateOutPaintingTaskResponse {private Output output;/*** 表示任務的輸出信息*/@Datapublic 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;}
查詢任務響應類:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class GetOutPaintingTaskResponse {/*** 請求唯一標識*/private String requestId;/*** 輸出信息*/private Output output;/*** 表示任務的輸出信息*/@Datapublic 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;}/*** 表示任務的統計信息*/@Datapublic static class TaskMetrics {/*** 總任務數*/private Integer total;/*** 成功任務數*/private Integer succeeded;/*** 失敗任務數*/private Integer failed;}
}
5)開發 ?API 調用類,通?過 Hutool ?的 HTTP 請求?工具類來調用阿里云?百煉的 API:
AI生成代碼小技巧2:
第一步,把發送請求樣例傳遞給AI
第二步,讓AI利用Hutool包發送請求生成代碼
@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 和擴圖參數:
@Data
public class CreatePictureOutPaintingTaskRequest implements Serializable {/*** 圖片 id*/private Long pictureId;/*** 擴圖參數*/private CreateOutPaintingTaskRequest.Parameters parameters;private static final long serialVersionUID = 1L;
}
在圖片服務中編寫創?建擴圖任務方法,從數據庫中獲取圖片?信息和 url 地址,構造請求參數?后調用 api 創建擴圖任務。注意?,如果圖片有空間 id,則需要校驗?權限,直接復用以前的權限校驗方法。
@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、擴圖接口
在 Pictu?reController 添?加 AI 擴圖接口,包括創建?任務和查詢任務狀態接口:???
/*** 創建 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接口進行測試,成功!