图生图

This commit is contained in:
spllzh
2025-10-19 13:45:53 +08:00
parent 9b2e08593c
commit a029d57e7d
28 changed files with 888 additions and 43 deletions

View File

@@ -172,6 +172,11 @@ public class PasswordUtil {

View File

@@ -156,6 +156,11 @@ public class ServiceManager {

View File

@@ -133,6 +133,11 @@ public class AliyunConfig {

View File

@@ -32,17 +32,11 @@ import org.springframework.web.client.RestTemplate;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.JsonNode;
import com.rj.service.MinIOService;
import java.io.*;
import java.net.URL;
/**
* 图像模型控制
*
* 提供图像模型相关的RESTful API接口包括
* - 图像模型记录的创建、查询、更新、删除操作
* - 分页查询和条件查询功能
* - 根据各种条件查询图像模型记录
* - 统一的响应格式和异常处理
* 图像模型控制
*
* https://help.aliyun.com/zh/model-studio/text-to-image#20e9dbb913b9s
*
* 使用Swagger注解进行API文档生成支持在线API测试。
* 所有接口都包含完整的参数验证、异常处理和日志记录。
@@ -678,9 +672,9 @@ public class ImageModelController {
}
}
@PostMapping("/image-gen-byText")
@PostMapping("/text-to-image")
@Operation(summary = "文本生成图像", description = "基于文本提示词生成图像")
public ResponseEntity<Map<String, Object>> imageGen(
public ResponseEntity<Map<String, Object>> textToImage(
@Parameter(description = "文本生成图像") @Valid @RequestBody TextModelController.ImageProcessingRequest request) {
log.info("开始文本生成图像request: {}", request);
@@ -906,36 +900,7 @@ public class ImageModelController {
}
}
/**
* 从URL下载图像
*
* @param imageUrl 图像URL
* @return 图像字节数组
* @throws IOException 下载失败时抛出异常
*/
private byte[] downloadImageFromUrl11(String imageUrl) throws IOException {
try {
log.info("开始下载图像: {}", imageUrl);
URL url = new URL(imageUrl);
try (InputStream inputStream = url.openStream();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
byte[] imageBytes = outputStream.toByteArray();
log.info("图像下载完成,大小: {} bytes", imageBytes.length);
return imageBytes;
}
} catch (Exception e) {
log.error("下载图像失败: {}", imageUrl, e);
throw new IOException("下载图像失败: " + e.getMessage(), e);
}
}
/**
@@ -1281,5 +1246,54 @@ public class ImageModelController {
private String generateRequestId() {
return "REQ_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 1000);
}
/**
* 调用阿里云 根据图片和指令,生成新图
*
* @return ResponseEntity 包含生成结果的响应实体
*/
@PostMapping("/image-to-image")
@Operation(summary = "图生图", description = "根据图片和指令,生成新图片")
public ResponseEntity<Map<String, Object>> generateimageToImage(
@Parameter(description = "文件", required = true)
@RequestParam("multipartFile") MultipartFile multipartFile,
@Parameter(description = "参考提示词", required = true)
@RequestParam("refPrompt") String refPrompt,
@Parameter(description = "提示词", required = true)
@RequestParam("prompt") String prompt,
@Parameter(description = "所属人姓名", required = true)
@RequestParam("ownerName") String ownerName,
@Parameter(description = "所属人电话", required = true)
@RequestParam("ownerPhone") String ownerPhone,
@Parameter(description = "图片名称", required = true)
@RequestParam("imageName") String imageName,
@Parameter(description = "模型名称", required = false)
@RequestParam(value = "model") String model,
@Parameter(description = "参数", required = false)
@RequestParam(value = "parameters", required = false) String parameters) {
try {
log.info("开始图生图处理ownerName: {}, imageName: {}, prompt: {}",
ownerName, imageName, prompt);
// 调用业务层方法处理图生图逻辑
Map<String, Object> result = imageModelService.generateImageToImage(
multipartFile, prompt, ownerName, ownerPhone, imageName, model, parameters);
return ResponseEntity.ok(result);
} catch (Exception e) {
log.error("图生图API失败: {}", e.getMessage(), e);
Map<String, Object> errorResult = new HashMap<>();
errorResult.put("success", false);
errorResult.put("message", "图生图生成失败: " + e.getMessage());
errorResult.put("error", e.getClass().getSimpleName());
errorResult.put("timestamp", LocalDateTime.now());
errorResult.put("requestId", generateRequestId());
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(errorResult);
}
}
}

View File

@@ -27,6 +27,7 @@ import com.alibaba.dashscope.aigc.generation.GenerationResult;
import com.alibaba.dashscope.aigc.generation.TranslationOptions;
import com.alibaba.dashscope.common.Message;
import com.alibaba.dashscope.common.Role;
import org.springframework.web.multipart.MultipartFile;
/**
@@ -124,7 +125,11 @@ public class TextModelController {
@NotBlank(message = "参考图片URL不能为空")
private String refImageUrl;
//上传的文件
private MultipartFile multipartFile;
@NotBlank(message = "参考提示词不能为空")
private String refPrompt;

View File

@@ -394,6 +394,11 @@ public class MenuController {

View File

@@ -364,6 +364,11 @@ public class RoleController {

View File

@@ -370,6 +370,11 @@ public class UserRoleController {

View File

@@ -136,6 +136,11 @@ public class DifyWorkflowResponseDto {

View File

@@ -0,0 +1,98 @@
package com.rj.dto;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* 阿里云海报生成请求DTO
*
* @author 系统生成
* @since 2025-01-30
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PosterGenerationRequestDto {
/**
* 模型名称
*/
private String model = "wanx-poster-generation-v1";
/**
* 输入参数
*/
private Input input;
/**
* 其他参数
*/
private java.util.Map<String, Object> parameters;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Input {
/**
* 标题
*/
private String title;
/**
* 副标题
*/
private String sub_title;
/**
* 正文内容
*/
private String body_text;
/**
* 中文提示词
*/
private String prompt_text_zh;
/**
* 宽高比
*/
private String wh_ratios;
/**
* LoRA模型名称
*/
private String lora_name;
/**
* LoRA权重
*/
private Double lora_weight;
/**
* 控制比例
*/
private Double ctrl_ratio;
/**
* 控制步数
*/
private Double ctrl_step;
/**
* 生成模式
*/
private String generate_mode;
/**
* 辅助参数
*/
private String auxiliary_parameters;
/**
* 生成数量
*/
private Integer generate_num;
}
}

View File

@@ -0,0 +1,68 @@
package com.rj.dto;
import lombok.Data;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
/**
* 阿里云海报生成响应DTO
*
* @author 系统生成
* @since 2025-01-30
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PosterGenerationResponseDto {
/**
* 输出结果
*/
private Output output;
/**
* 请求ID
*/
private String request_id;
/**
* 使用情况
*/
private Usage usage;
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Output {
/**
* 任务ID
*/
private String task_id;
/**
* 任务状态
*/
private String task_status;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
public static class Usage {
/**
* 输入token数
*/
private Integer input_tokens;
/**
* 输出token数
*/
private Integer output_tokens;
/**
* 总token数
*/
private Integer total_tokens;
}
}

View File

@@ -79,6 +79,11 @@ public interface CustomerProfileAnalysisMapper extends BaseMapper<CustomerProfil

View File

@@ -156,4 +156,27 @@ public interface IImageModelService extends IService<ImageModel> {
* @return 生成结果包含图像URL等信息
*/
Map<String, Object> generateImageByText(TextModelController.ImageProcessingRequest request);
/**
* 图生图
*
* 根据图片和指令生成新图片,包括:
* - 验证API Key和参数
* - 构建请求体
* - 调用阿里云图生图API
* - 处理异步响应
* - 返回任务信息
*
* @param multipartFile 上传的图片文件
* @param refPrompt 参考提示词
* @param ownerName 所属人姓名
* @param ownerPhone 所属人电话
* @param imageName 图片名称
* @param model 模型名称默认为wanx-v1
* @param parameters 参数字符串JSON格式
* @return 生成结果包含任务ID等信息
*/
Map<String, Object> generateImageToImage(org.springframework.web.multipart.MultipartFile multipartFile,
String refPrompt, String ownerName, String ownerPhone,
String imageName, String model, String parameters);
}

View File

@@ -100,6 +100,11 @@ public interface ICustomerProfileAnalysisService extends IService<CustomerProfil

View File

@@ -135,6 +135,11 @@ public class CustomerProfileAnalysisServiceImpl extends ServiceImpl<CustomerProf

View File

@@ -1,6 +1,5 @@
package com.rj.service.impl;
import ai.djl.util.JsonUtils;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesis;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisParam;
import com.alibaba.dashscope.aigc.imagesynthesis.ImageSynthesisResult;
@@ -13,6 +12,7 @@ import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.exception.UploadFileException;
import com.alibaba.dashscope.utils.Constants;
import com.alibaba.dashscope.utils.JsonUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.rj.controller.TextModelController;
@@ -24,11 +24,14 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.*;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.*;
import java.util.Arrays;
import java.util.Collections;
/**
* 图像模型服务实现类
@@ -53,6 +56,9 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
@Autowired
private MinIOService minIOService;
@Autowired
private ObjectMapper objectMapper;
/**
* 保存图像模型记录
@@ -1085,4 +1091,271 @@ public class ImageModelServiceImpl extends ServiceImpl<ImageModelMapper, ImageMo
}
}
/**
* 图生图
*
* 根据图片和指令生成新图片,包括:
* - 验证API Key和参数
* - 构建请求体
* - 调用阿里云图生图API
* - 处理异步响应
* - 返回任务信息
*
* @param multipartFile 上传的图片文件
* @param ownerName 所属人姓名
* @param ownerPhone 所属人电话
* @param imageName 图片名称
* @param model 模型名称默认为wanx-v1
* @param parameters 参数字符串JSON格式
* @return 生成结果包含任务ID等信息
*/
@Override
public Map<String, Object> generateImageToImage(MultipartFile multipartFile, String prompt,
String ownerName, String ownerPhone, String imageName,
String model, String parameters) {
Map<String, Object> result = new HashMap<>();
try {
log.info("开始图生图处理ownerName: {}, imageName: {}, refPrompt: {}",
ownerName, imageName, prompt);
// 1. 验证API Key
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
throw new RuntimeException("DASHSCOPE_API_KEY 环境变量未设置");
}
// 2. 上传文件到MinIO并生成临时访问URL
log.info("开始上传文件到MinIO文件名: {}", multipartFile.getOriginalFilename());
// 生成唯一文件名
String originalFilename = multipartFile.getOriginalFilename();
String fileExtension = getFileExtension(originalFilename);
String uniqueFileName = generateUniqueFileName(fileExtension);
// 上传到MinIO
String materialUrl = minIOService.uploadFileWithName(multipartFile, uniqueFileName);
String materialTempUrl = minIOService.generateTempUrl(uniqueFileName);
log.info("文件上传到MinIO成功:");
log.info("永久URL: {}", materialUrl);
log.info("临时访问URL: {}", materialTempUrl);
// 输出到控制台
System.out.println("=== 图生图文件上传信息 ===");
System.out.println("原始文件名: " + originalFilename);
System.out.println("生成的文件名: " + uniqueFileName);
System.out.println("MinIO永久URL: " + materialUrl);
System.out.println("MinIO临时访问URL: " + materialTempUrl);
System.out.println("=========================");
// 3. 创建ImageModel记录并保存到数据库
ImageModel imageModel = new ImageModel();
imageModel.setImageName(imageName != null ? imageName : originalFilename);
imageModel.setOwnerName(ownerName);
imageModel.setOwnerPhone(ownerPhone);
imageModel.setImageType("material");
imageModel.setPrompt(prompt);
imageModel.setModelName(model);
imageModel.setMaterialUrl(materialUrl);
imageModel.setMaterialTempUrl(materialTempUrl);
imageModel.setCreateTime(LocalDateTime.now());
imageModel.setUpdateTime(LocalDateTime.now());
// 保存到数据库
boolean saveResult = this.saveImageModel(imageModel);
if (!saveResult) {
throw new RuntimeException("保存图生图记录失败");
}
log.info("图生图记录已保存到数据库ID: {}", imageModel.getUuid());
// 4. 设置API基础URL
Constants.baseHttpApiUrl = "https://dashscope.aliyuncs.com/api/v1";
// 5. 使用MultiModalConversation调用阿里云API
MultiModalConversation conv = new MultiModalConversation();
// 构建多模态消息
MultiModalMessage userMessage = MultiModalMessage.builder()
.role(Role.USER.getValue())
.content(Arrays.asList(
Collections.singletonMap("image", materialTempUrl), // 使用临时访问URL
Collections.singletonMap("text", prompt)
)).build();
// 解析parameters参数
Map<String, Object> parametersMap = new HashMap<>();
if (parameters != null && !parameters.isEmpty()) {
try {
@SuppressWarnings("unchecked")
Map<String, Object> parsedParams = objectMapper.readValue(parameters, Map.class);
parametersMap = parsedParams;
} catch (Exception e) {
log.warn("解析parameters参数失败使用默认值: {}", e.getMessage());
}
}
// 设置默认参数
parametersMap.put("watermark", false);
if (!parametersMap.containsKey("negative_prompt")) {
parametersMap.put("negative_prompt", " ");
}
// 构建API参数
MultiModalConversationParam param = MultiModalConversationParam.builder()
.apiKey(apiKey)
.model(model) // 使用图生图模型
.messages(Collections.singletonList(userMessage))
.parameters(parametersMap)
.build();
// 输出请求参数到控制台
System.out.println("=== 发送给阿里云的请求参数 ===");
System.out.println("模型: qwen-image-edit");
System.out.println("图片URL: " + materialTempUrl);
System.out.println("提示词: " + prompt);
System.out.println("参数: " + JsonUtils.toJson(parametersMap));
System.out.println("=========================");
// 6. 调用阿里云API
log.info("开始调用阿里云图生图API");
MultiModalConversationResult conversationResult = conv.call(param);
// 输出完整响应到控制台
System.out.println("=== 阿里云API完整响应 ===");
System.out.println(JsonUtils.toJson(conversationResult));
System.out.println("======================");
// 7. 处理生成结果
if (conversationResult != null && conversationResult.getOutput() != null &&
conversationResult.getOutput().getChoices() != null &&
!conversationResult.getOutput().getChoices().isEmpty()) {
log.info("图生图API调用成功开始处理结果");
// 从多模态对话结果中提取图像URL
String generatedImageUrl = null;
try {
var choices = conversationResult.getOutput().getChoices();
if (!choices.isEmpty()) {
var message = choices.get(0).getMessage();
var content = message.getContent();
log.info("API返回的content: {}", JsonUtils.toJson(content));
// 解析content数组中的image字段
if (content != null && !content.isEmpty()) {
for (Object contentItem : content) {
if (contentItem instanceof Map) {
@SuppressWarnings("unchecked")
Map<String, Object> contentMap = (Map<String, Object>) contentItem;
if (contentMap.containsKey("image")) {
generatedImageUrl = (String) contentMap.get("image");
log.info("提取到生成图像URL: {}", generatedImageUrl);
break;
}
}
}
}
}
if (generatedImageUrl == null || generatedImageUrl.trim().isEmpty()) {
log.warn("未能从API返回结果中提取图像URL");
throw new RuntimeException("未能从API返回结果中提取图像URL");
}
} catch (Exception e) {
log.error("解析API返回结果失败", e);
throw new RuntimeException("解析API返回结果失败: " + e.getMessage(), e);
}
// 8. 下载生成的图片并上传到MinIO
if (generatedImageUrl != null && !generatedImageUrl.trim().isEmpty()) {
try {
log.info("开始下载生成的图片: {}", generatedImageUrl);
// 下载图片到字节数组
byte[] imageBytes = downloadImageToBytes(generatedImageUrl);
log.info("图片下载成功,大小: {} bytes", imageBytes.length);
// 生成唯一文件名
String resultFileName = generateUniqueFileName("png");
// 上传到MinIO
String resultImageMinIOUrl = minIOService.uploadFile(imageBytes, resultFileName, "image/png");
log.info("图片上传到MinIO成功: {}", resultImageMinIOUrl);
// 生成7天临时访问链接
String resultImageTempUrl = minIOService.generateTempUrl(resultFileName);
log.info("生成7天临时访问链接: {}", resultImageTempUrl);
// 更新ImageModel对象
imageModel.setResultImageUrl(resultImageMinIOUrl);
imageModel.setResultImageTempUrl(resultImageTempUrl);
imageModel.setUpdateTime(LocalDateTime.now());
imageModel.setImageType("result");
// 更新数据库记录
boolean updateResult = this.updateById(imageModel);
if (!updateResult) {
log.error("更新图生图结果记录失败");
} else {
log.info("图生图结果已更新到数据库");
}
// 构建返回结果
result.put("success", true);
result.put("message", "图生图处理完成");
result.put("imageCount", 1);
result.put("imageUrl", resultImageTempUrl);
result.put("imageMinIOUrl", resultImageMinIOUrl);
result.put("originalImageUrl", materialTempUrl);
result.put("prompt", model);
result.put("model", model);
result.put("requestId", imageModel.getUuid());
result.put("timestamp", LocalDateTime.now());
log.info("✅ 图生图处理完成结果已保存到数据库ID: {}", imageModel.getUuid());
} catch (Exception e) {
log.error("处理生成的图片失败", e);
throw new RuntimeException("处理生成的图片失败: " + e.getMessage(), e);
}
} else {
log.warn("生成的图片URL为空");
throw new RuntimeException("生成的图片URL为空");
}
} else {
log.warn("图生图结果为空");
throw new RuntimeException("图生图结果为空");
}
return result;
} catch (ApiException | NoApiKeyException | UploadFileException e) {
log.error("阿里云API调用失败", e);
throw new RuntimeException("阿里云API调用失败: " + e.getMessage(), e);
} catch (Exception e) {
log.error("图生图处理失败: {}", e.getMessage(), e);
result.put("success", false);
result.put("message", "图生图处理失败: " + e.getMessage());
result.put("error", e.getClass().getSimpleName());
result.put("timestamp", LocalDateTime.now());
result.put("requestId", generateRequestId());
throw new RuntimeException("图生图处理失败: " + e.getMessage(), e);
}
}
/**
* 生成请求ID
*
* @return 唯一的请求ID
*/
private String generateRequestId() {
return "req_" + System.currentTimeMillis() + "_" + UUID.randomUUID().toString().substring(0, 8);
}
}

View File

@@ -107,6 +107,11 @@ public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IM

View File

@@ -107,6 +107,11 @@ public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements IR

View File

@@ -107,6 +107,11 @@ public class UserRoleServiceImpl extends ServiceImpl<UserRoleMapper, UserRole> i

View File

@@ -116,6 +116,11 @@ spring:

View File

@@ -292,6 +292,11 @@

View File

@@ -0,0 +1,196 @@
package com.rj.controller;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class PosterGenerationTest {
private static final Logger log = LoggerFactory.getLogger(PosterGenerationTest.class);
private final RestTemplate restTemplate = new RestTemplate();
private final ObjectMapper objectMapper = new ObjectMapper();
public static void main(String[] args) {
System.out.println("程序开始运行...");
try {
new PosterGenerationTest().testPosterGeneration();
} catch (Exception e) {
System.out.println("程序运行出错: " + e.getMessage());
e.printStackTrace();
}
System.out.println("程序结束");
}
public void testPosterGeneration() {
try {
System.out.println("开始测试阿里云海报生成API");
log.info("开始测试阿里云海报生成API");
// 验证API Key
String apiKey = System.getenv("DASHSCOPE_API_KEY");
if (apiKey == null || apiKey.isEmpty()) {
System.out.println("DASHSCOPE_API_KEY 环境变量未设置");
log.error("DASHSCOPE_API_KEY 环境变量未设置");
return;
}
System.out.println("API Key 已设置,开始构建请求...");
System.out.println("API Key: " + apiKey.substring(0, 10) + "...");
// 构建请求体 - 使用阿里云官方文档的标准格式
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", "wanx-poster-generation-v1");
// 设置输入参数 - 使用完整的官方参数
Map<String, Object> input = new HashMap<>();
input.put("title", "春节快乐");
input.put("sub_title", "家庭团聚,共享天伦之乐");
input.put("body_text", "春节是中国最重要的传统节日之一,它象征着新的开始和希望");
input.put("prompt_text_zh", "灯笼,小猫,梅花");
input.put("wh_ratios", "竖版");
input.put("lora_name", "童话油画");
input.put("lora_weight", 0.8);
input.put("ctrl_ratio", 0.7);
input.put("ctrl_step", 0.7);
input.put("generate_mode", "generate");
// auxiliary_parameters 在首次调用时应该不传或者传空字符串
// input.put("auxiliary_parameters", "[]");
input.put("generate_num", 1);
requestBody.put("input", input);
requestBody.put("parameters", new HashMap<>());
// 构建请求头
HttpHeaders headers = new HttpHeaders();
headers.set("X-DashScope-Async", "enable"); // 启用异步调用
headers.set("Authorization", "Bearer " + apiKey);
headers.setContentType(MediaType.APPLICATION_JSON);
// 创建HTTP实体
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
// 发送请求
String apiUrl = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text2image/image-synthesis";
System.out.println("发送请求到阿里云海报生成API: " + apiUrl);
System.out.println("请求体: " + objectMapper.writeValueAsString(requestBody));
log.info("发送请求到阿里云海报生成API: {}", apiUrl);
log.info("请求体: {}", objectMapper.writeValueAsString(requestBody)); // 打印请求体
ResponseEntity<String> response = restTemplate.exchange(
apiUrl,
HttpMethod.POST,
entity,
String.class
);
// 处理响应
System.out.println("响应状态码: " + response.getStatusCode());
System.out.println("响应体: " + response.getBody());
log.info("响应状态码: {}", response.getStatusCode());
log.info("响应体: {}", response.getBody());
if (response.getStatusCode().is2xxSuccessful()) {
System.out.println("✅ 阿里云海报生成API调用成功");
log.info("✅ 阿里云海报生成API调用成功");
// 解析响应获取task_id
JsonNode responseNode = objectMapper.readTree(response.getBody());
JsonNode outputNode = responseNode.path("output");
if (!outputNode.isMissingNode() && outputNode.has("task_id")) {
String taskId = outputNode.path("task_id").asText();
String taskStatus = outputNode.path("task_status").asText();
log.info("任务ID: {}", taskId);
log.info("任务状态: {}", taskStatus);
// 轮询获取任务结果
getTaskResult(apiKey, taskId);
} else {
log.error("❌ 响应中未找到task_id或output节点");
}
} else {
log.error("❌ 阿里云海报生成API调用失败状态码: {}", response.getStatusCode());
log.error("❌ 响应详情: {}", response.getBody());
}
} catch (Exception e) {
log.error("测试阿里云海报生成API失败: {}", e.getMessage(), e);
}
}
/**
* 轮询获取任务结果
* @param apiKey API Key
* @param taskId 任务ID
*/
private void getTaskResult(String apiKey, String taskId) {
String resultUrl = "https://dashscope.aliyuncs.com/api/v1/tasks/" + taskId;
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + apiKey);
HttpEntity<String> entity = new HttpEntity<>(headers);
int maxRetries = 30;
int intervalSeconds = 5;
for (int i = 0; i < maxRetries; i++) {
try {
ResponseEntity<String> response = restTemplate.exchange(resultUrl, HttpMethod.GET, entity, String.class);
if (response.getStatusCode().is2xxSuccessful()) {
JsonNode resultNode = objectMapper.readTree(response.getBody());
JsonNode statusNode = resultNode.path("output").path("task_status");
if (!statusNode.isMissingNode()) {
String status = statusNode.asText();
log.info("当前任务状态: {}", status);
if ("SUCCEEDED".equalsIgnoreCase(status)) {
log.info("🎉 任务执行成功!");
log.info("任务结果详情: {}", response.getBody());
return;
} else if ("FAILED".equalsIgnoreCase(status)) {
log.error("❌ 任务执行失败!");
log.error("失败详情: {}", response.getBody());
// 如果失败,打印更详细的错误信息
JsonNode messageNode = resultNode.path("output").path("message");
if (!messageNode.isMissingNode()) {
log.error("失败具体原因: {}", messageNode.asText());
}
return;
}
// 如果是 RUNNING 或其他中间状态,继续轮询
} else {
log.error("❌ 获取任务状态失败,响应中缺少 task_status 字段。");
log.error("响应内容: {}", response.getBody());
return;
}
} else {
log.error("❌ 查询任务状态失败,状态码: {}", response.getStatusCode());
log.error("响应内容: {}", response.getBody());
}
} catch (Exception e) {
log.error("轮询任务结果时发生异常: {}", e.getMessage(), e);
}
log.info("等待 {} 秒后继续轮询...", intervalSeconds);
try {
TimeUnit.SECONDS.sleep(intervalSeconds);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
log.error("轮询被中断");
return;
}
}
log.error("❌ 超时,未能获取最终任务结果。");
}
}

View File

@@ -0,0 +1,68 @@
package com.rj.image;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversation;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationParam;
import com.alibaba.dashscope.aigc.multimodalconversation.MultiModalConversationResult;
import com.alibaba.dashscope.common.MultiModalMessage;
import com.alibaba.dashscope.common.Role;
import com.alibaba.dashscope.exception.ApiException;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.alibaba.dashscope.exception.UploadFileException;
import com.alibaba.dashscope.utils.Constants;
import com.alibaba.dashscope.utils.JsonUtils;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
public class QwenImageEdit {
static {
// 以下为中国北京地域url若使用新加坡地域的模型需将url替换为https://dashscope-intl.aliyuncs.com/api/v1
Constants.baseHttpApiUrl = "https://dashscope.aliyuncs.com/api/v1";
}
// 新加坡和北京地域的API Key不同。获取API Keyhttps://help.aliyun.com/zh/model-studio/get-api-key
// 若没有配置环境变量,请用百炼 API Key 将下行替换为apiKey="sk-xxx"
static String apiKey = System.getenv("DASHSCOPE_API_KEY");
public static void call() throws ApiException, NoApiKeyException, UploadFileException, IOException {
MultiModalConversation conv = new MultiModalConversation();
// 模型支持输入1-3张图片
MultiModalMessage userMessage = MultiModalMessage.builder().role(Role.USER.getValue())
.content(Arrays.asList(
Collections.singletonMap("image", "http://101.35.52.237:19005/car/1760778801381_925c0074cde844d8bf6eeee7f98723e4.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20251018%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20251018T091321Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=e99e8e4d023c452af18961c9ee93c70b589f6ae575a2190b13d3a4c0a6c818f3"),
Collections.singletonMap("image", "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250925/iclsnx/input2.png"),
Collections.singletonMap("image", "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20250925/gborgw/input3.png"),
Collections.singletonMap("text", "图1中的女生穿着图2中的黑色裙子按图3的姿势坐下")
)).build();
Map<String, Object> parameters = new HashMap<>();
parameters.put("watermark", false);
parameters.put("negative_prompt", " ");
MultiModalConversationParam param = MultiModalConversationParam.builder()
.apiKey(apiKey)
.model("qwen-image-edit")
.messages(Collections.singletonList(userMessage))
.parameters(parameters)
.build();
MultiModalConversationResult result = conv.call(param);
// 如需查看完整响应,请取消下行注释
// System.out.println(JsonUtils.toJson(result));
System.out.println("输出图像的URL" + result.getOutput().getChoices().get(0).getMessage().getContent().get(0).get("image"));
}
public static void main(String[] args) {
try {
call();
} catch (ApiException | NoApiKeyException | UploadFileException | IOException e) {
System.out.println(e.getMessage());
}
}
}

View File

@@ -230,6 +230,11 @@ public class FaceDetectImageCountTest {

View File

@@ -171,6 +171,11 @@ public class TtsRequestLogShortUrlTest {

View File

@@ -149,6 +149,11 @@ public class VideoSynthesisTempUrlTest {

View File

@@ -124,6 +124,11 @@ public class VideoSynthesisVideoNameTest {