Spring-ai-alibaba文生图

文章目录

Spring AI Alibaba 图像模型实战(文生图)

用 Spring AI Alibaba 接入阿里云百炼 DashScope,完成「文字 → 图片」。

版本

  • Spring Boot:3.4.5
  • spring-ai-alibaba-starter-dashscope:1.0.0.4
  • Java:17

Spring-ai更新速度快,请以官方为准

可参考网址:

一、依赖引入

xml 复制代码
<!-- Spring Web,提供 REST 接口 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring AI Alibaba:自动装配 DashScopeImageModel -->
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    <version>1.0.0.4</version>
</dependency>

<!-- 官方 DashScope SDK(部分场景会用到) -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dashscope-sdk-java</artifactId>
    <version>2.22.18</version>
</dependency>

二、yml 配置

yaml 复制代码
# 复制为 application.yaml;密钥用环境变量,不要写死在仓库里
spring:
  application:
    name: spring-image-demo
  ai:
    dashscope:
      # IDEA Run Configuration → Environment:DASHSCOPE_API_KEY=sk-xxx
      api-key: ${DASHSCOPE_API_KEY}
      image:
        options:
          # DashScopeImageModel 兼容的万相文生图模型
          # 不要用 qwen-image-* 等多模态同步模型,会报 url error / 400
          model: wanx2.1-t2i-turbo

启动前设置环境变量:

powershell 复制代码
$env:DASHSCOPE_API_KEY="你的百炼API Key"

三、代码案例

1. 控制器:注入 ImageModel,三种接口

java 复制代码
@RestController
@RequestMapping("/example")
public class DashScopeImageController {

    /** 由 starter 自动配置注入(底层是 DashScopeImageModel) */
    private final ImageModel imageModel;

    private static final String DEFAULT_PROMPT = "为人工智能生成一张富有科技感的图片!";

    public DashScopeImageController(ImageModel imageModel) {
        this.imageModel = imageModel;
    }

    /**
     * 最简文生图:用默认 prompt 生成一张图,把远端 URL 的字节写回浏览器。
     * GET /example/image
     */
    @GetMapping("/image")
    public void image(HttpServletResponse response) {
        // 1)构造 ImagePrompt(只有提示词,模型走 yml 默认配置)
        ImageResponse imageResponse = imageModel.call(new ImagePrompt(DEFAULT_PROMPT));

        // 2)异步任务完成后,结果里一般是临时图片 URL
        String imageUrl = imageResponse.getResult().getOutput().getUrl();

        try {
            // 3)服务端打开 URL,把 PNG 字节写给前端(浏览器直接显示图片)
            URL url = URI.create(imageUrl).toURL();
            InputStream in = url.openStream();
            response.setHeader("Content-Type", MediaType.IMAGE_PNG_VALUE);
            response.getOutputStream().write(in.readAllBytes());
            response.getOutputStream().flush();
        } catch (IOException e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * 同一 prompt 生成多张图,返回 URL 集合。
     * GET /example/image/multiPrompt?prompt=一只会编程的猫&count=2
     */
    @GetMapping("/image/multiPrompt")
    public ResponseEntity<Collection<String>> generateImageWithMultiPrompt(
            @RequestParam(value = "prompt", defaultValue = "一只会编程的猫") String prompt,
            @RequestParam(defaultValue = "2") int count) {

        // N:一次要几张图
        ImageOptions options = ImageOptionsBuilder.builder()
                .N(count)
                .build();

        ImageResponse response = imageModel.call(new ImagePrompt(prompt, options));

        // 每张结果取 URL,收集成 Set
        Set<String> imageSet = response.getResults().stream()
                .map(result -> result.getOutput().getUrl())
                .collect(Collectors.toSet());
        return ResponseEntity.ok(imageSet);
    }

    /**
     * 多条件拼 prompt + 宽高。
     * GET /example/image/multipleConditions?subject=猫&environment=办公室&style=生动
     */
    @GetMapping("/image/multipleConditions")
    public ResponseEntity<?> multipleConditions(
            @RequestParam(value = "subject", defaultValue = "一只会编程的猫") String subject,
            @RequestParam(value = "environment", defaultValue = "办公室") String environment,
            @RequestParam(value = "height", defaultValue = "1024") Integer height,
            @RequestParam(value = "width", defaultValue = "1024") Integer width,
            @RequestParam(value = "style", defaultValue = "生动") String style) {

        // 把多个参数拼成一句完整提示词
        String prompt = String.format(
                "一个%s,置身于%s的环境中,使用%s的艺术风格,高清4K画质,细节精致",
                subject, environment, style
        );

        ImageOptions options = ImageOptionsBuilder.builder()
                .height(height)
                .width(width)
                .build();

        try {
            ImageResponse response = imageModel.call(new ImagePrompt(prompt, options));
            // 成功时直接返回图片 URL 字符串
            return ResponseEntity.ok(response.getResult().getOutput().getUrl());
        } catch (Exception e) {
            // 失败时返回 JSON 错误信息,避免空白 500
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of(
                            "error", "图像生成失败",
                            "message", e.getMessage(),
                            "timestamp", LocalDateTime.now()
                    ));
        }
    }
}

2.(可选)图片未完成时的重试

万相文生图是异步任务。旧版在 pending 时可能抛 RuntimeException,默认 Retry 可能不够。可提供自定义 RetryTemplate

java 复制代码
@Configuration
public class ImageRetryConfig {

    @Bean
    @Primary
    RetryTemplate imageRetryTemplate() {
        return RetryTemplate.builder()
                .maxAttempts(30) // 最多重试次数
                // 初始间隔 2s,倍率 1.5,上限 15s
                .exponentialBackoff(Duration.ofSeconds(2), 1.5, Duration.ofSeconds(15))
                .retryOn(RuntimeException.class)
                .build();
    }
}

注意:HTTP 401(Key 无效)、400(参数错误)属于不可恢复错误,靠加长重试解决不了。

相关推荐
营养充电站1 小时前
KMP全栈开发:从Android到AI Agent的技术演进与实践
人工智能·算法·docker·jupyter
程序员cxuan1 小时前
速度太快了!本地可以跑 DeepSeek-V4-Flash 了
人工智能·后端·程序员
Claire_881 小时前
AI 辅助 PPT 生成工具横向测评:从模板库到多模态生成的选型参考
人工智能·powerpoint
半亩码田2 小时前
AI周报 | DeepSeek-V4-Flash 正式版(7-31)、Wan2.6 全链路、GPT-5.6 降价 80%(上周 07.27-08.02)
人工智能·gpt
山东布谷网络科技2 小时前
靠“社交+游戏”突围:中东语聊APP前景预测与低成本运营案例
人工智能·游戏
技术小黑2 小时前
RNN算法实战系列05 | 天气预测
人工智能·rnn·算法
带娃的IT创业者2 小时前
GitHub 热门项目解析:当 AI 编码助手遭遇“上下文爆炸”
人工智能·github·代码优化·ai编程助手·上下文窗口·上下文压缩
SimLine芯见2 小时前
以可靠性为中心的RCM KVM远程管控在多行业高端制造中的应用
大数据·人工智能·制造
前端开发江鸟2 小时前
Agent 已经能跑起来了,我却不知道怎样判断它好不好
人工智能