【第38篇】DashScope 图像生成
用 Spring Boot 轻松玩转阿里云通义万相 AI 绘画 🎨
前言
只需要几句描述词,后台就能自动生成一张精美的图片——这就是 AI 图像生成的魅力。不过,要把这项能力集成到 Java 项目里,以往可能需要研究各种 SDK、处理复杂的鉴权和流协议。
Spring AI Alibaba 把调用阿里云 DashScope 通义万相的复杂细节封装成统一的接口,让你像调用普通 Bean 一样生成图像。本指南将带着你,从底层设计原理,到三个实战功能,再到生产环境部署,一步一步把这个示例吃透、跑起来。
一、项目概览
1.1 概述
这是一个基于 Spring Boot 3 + Spring AI Alibaba 的轻量级图像生成示例应用。它提供了三个 REST 接口,覆盖了从简单到复杂的图像生成需求:
- 基础生成:说句话,直接返回一张图
- 批量生成:一句话生成多张变体
- 多条件定制:分别指定主体、环境、风格,按需拼出精细化的图像
项目启动在 10008 端口,所有实验都可以用 curl 或浏览器完成。
1.2 技术栈速览
| 组件 | 作用 |
|---|---|
| Spring Boot 3.x | 应用框架 |
| Spring AI Alibaba Starter | 统一 AI 模型抽象,对接 DashScope |
| DashScope API(通义万相) | 实际执行图像生成的云端服务 |
| RESTful API | 提供 HTTP 交互 |
整个项目只有一个核心依赖:spring-ai-alibaba-starter-dashscope,它已经替你管理好了和阿里云通信的全部逻辑。
二、核心原理:Spring AI 如何帮你画图
2.1 统一抽象:ImageModel 和 ImagePrompt
Spring AI 设计的精髓在于“换个模型只是换个实现”。对于图像生成,它定义了:
ImageModel接口:代表一个能生成图像的模型,核心方法是call(ImagePrompt request)。ImagePrompt:包含了提示词和你想要的各种选项(尺寸、数量、风格等)。ImageResponse:模型返回的结果,其中包含生成图片的 URL 或 Base64 数据。
你只需要注入一个 ImageModel,传入 ImagePrompt,就能得到结果。底层到底是通义万相、OpenAI DALL·E 还是 Stability AI,对你来说都变成同一套代码。
2.2 DashScope 是怎么接入的?
当你引入 spring-ai-alibaba-starter-dashscope 后,Spring Boot 会自动配置一个 DashScopeImageModel 实例,它就是 ImageModel 的一种实现。配置好 API Key 后,ImageModel.call() 最终会转换成对阿里云 DashScope 的一次 HTTP POST 请求,就像这样:
POST https://dashscope.aliyuncs.com/api/v1/images/generation
Headers:
Authorization: Bearer sk-xxxxxxxxxxx
Body:
{
"model": "wanx-v1",
"input": {
"prompt": "一只会编程的猫"
},
"parameters": {
"n": 2,
"size": "1024*1024"
}
}
2.3 一次图像请求的完整旅程
下面这个序列图展示了从前端发起请求到最终拿到图片的全过程:
可以看到,Controller 并没有直接操作 HTTP 详情,它只是调用了 Spring AI 提供的 ImageModel。这种分层让代码干净、好测试,未来切换其他图像模型也非常容易。
三、架构设计全景图
整个应用从外到内分为清晰的四层:
- Web 层:处理请求参数,拼装提示词,并将最终图片写入 HTTP 响应。
- Spring AI 层:提供
ImageModel抽象和ImageOptionsBuilder构建工具,隔离底层实现。 - 云服务层:实际执行 AI 推理,生成图片并返回临时下载地址。
下面,我们就深入到三个功能中,看看每一层具体怎么交互。
四、功能逐一拆解
4.1 功能一:基础图像生成
接口:GET /example/image
参数:无(使用默认提示词)
返回:PNG 图片流
这是最简答的“说一句话,得一张图”。当你访问这个地址时,后端会用科技感满满的默认提示词去请求生成。具体步骤可以用流程图来看:
代码实现(优化后,采用流式传输避免大图 OOM):
@GetMapping("/example/image")
public void image(HttpServletResponse response) throws IOException {
// 1. 构建请求,使用默认提示词
ImagePrompt prompt = new ImagePrompt(DEFAULT_PROMPT);
// 2. 调用模型
ImageResponse imageResponse = imageModel.call(prompt);
String imageUrl = imageResponse.getResult().getOutput().getUrl();
// 3. 设置响应头
response.setContentType(MediaType.IMAGE_PNG_VALUE);
// 4. 流式读取并写入,避免全量加载到内存
try (InputStream in = new URL(imageUrl).openStream();
OutputStream out = response.getOutputStream()) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
out.flush();
}
}
设计亮点:
- 使用缓冲区(8KB)循环读写,即使图片很大也不会撑爆内存。
- 提示词可以抽成常量或配置,方便后续改动。
一笔带过但想补充的:
原示例中直接 in.readAllBytes() 写入内存,这里我们已经改成了流式拷贝,生产环境更推荐这种写法。
4.2 功能二:一次生成多张变体
接口:GET /example/image/multiPrompt
参数:
prompt:提示词(默认:“一只会编程的猫”)count:数量(默认:2)
返回:Collection<String>(图片 URL 集合)
有些时候,同一个点子你想要多张不同构图的版本,这个接口就是干这个的。关键在于用 ImageOptionsBuilder 设置生成数量 N(count):
@GetMapping("/example/image/multiPrompt")
public Collection<String> generateMultiImages(
@RequestParam(defaultValue = "一只会编程的猫") String prompt,
@RequestParam(defaultValue = "2") int count) {
ImageOptions options = ImageOptionsBuilder.builder()
.N(count) // 希望生成的数量
.build();
ImageResponse response = imageModel.call(new ImagePrompt(prompt, options));
return response.getResults().stream()
.map(result -> result.getOutput().getUrl())
.collect(Collectors.toSet()); // Set 自动去重
}
工作流程:
一点提醒:
阿里云 DashScope 对批量数量通常限制在 1~4 张,超过会报错。所以别指望一次生成几十张。
4.3 功能三:多条件精细控制
接口:GET /example/image/multipleConditions
参数:
subject:主体(如“一只会编程的猫”)environment:环境(如“办公室”)width/height:尺寸(默认 1024x1024)style:艺术风格(如“生动”“赛博朋克”)
返回:图片 URL 或结构化的错误 JSON。
这个接口体现了实战中最常见的场景:让用户自定义主体、背景和画风,后端拼成一段完整的、高质量的提示词。例如:
String prompt = String.format(
"一个%s,置身于%s的环境中,使用%s的艺术风格,高清4K画质,细节精致",
subject, environment, style
);
然后根据参数设置图片尺寸:
ImageOptions options = ImageOptionsBuilder.builder()
.height(height)
.width(width)
.build();
错误处理:
如果生成失败(网络问题、API Key 无效、敏感词触发审核等),接口不直接裸抛异常,而是返回给前端一个友好的 JSON:
{
"error": "图像生成失败",
"message": "具体错误信息",
"timestamp": "2026-05-06T12:34:56"
}
实现上,它把异常捕获后包装成“结构化错误响应”。更好的做法是抽取一个全局异常处理器,这样每个接口不用反复写 try-catch。我给你补一个推荐写法:
// 自定义异常
public class ImageGenerationException extends RuntimeException {
public ImageGenerationException(String message) { super(message); }
}
// 全局处理器
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(ImageGenerationException.class)
public ResponseEntity<Map<String, Object>> handleImageException(ImageGenerationException e) {
Map<String, Object> body = new LinkedHashMap<>();
body.put("error", "图像生成失败");
body.put("message", e.getMessage());
body.put("timestamp", LocalDateTime.now());
return ResponseEntity.internalServerError().body(body);
}
}
这样,Controller 里只需:
try {
// 图像生成逻辑
} catch (Exception e) {
throw new ImageGenerationException("生成失败:" + e.getMessage());
}
代码更清爽,也方便统一记录日志。
五、配置与扩展
5.1 最小化配置
在 application.yml 里,你只需要两样东西:
server:
port: 10008
spring:
application:
name: spring-ai-alibaba-dashscope-image-example
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} # 从环境变量读取
${AI_DASHSCOPE_API_KEY} 会从系统环境变量中取值。本地开发时,设置方式如下:
# Linux / Mac
export AI_DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx
# Windows PowerShell
$env:AI_DASHSCOPE_API_KEY="sk-xxxxxxxxxxxxxxxx"
5.2 进阶配置
有时候你需要更细粒度的控制,比如切换模型、调超时:
spring:
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY}
image:
options:
model: wanx-v1 # 指定模型,也可用 wanx-plus
connection-timeout: 30s # 连接超时
read-timeout: 120s # 读取超时(图像生成可能需要更久)
5.3 异步与流式返回
默认调用是同步的,即请求一直等待直到 DashScope 返回。如果生成时间较长,客户端也会被阻塞。你可以用 CompletableFuture 快速让接口变成异步:
@GetMapping("/image/async")
public CompletableFuture<ResponseEntity<String>> generateAsync(@RequestParam String prompt) {
return CompletableFuture.supplyAsync(() -> {
ImageResponse res = imageModel.call(new ImagePrompt(prompt));
return ResponseEntity.ok(res.getResult().getOutput().getUrl());
});
}
你也可以借助 Spring WebFlux 的 SSE(Server-Sent Events)实时推送生成进度(这里简化成返回 URL):
@GetMapping(value = "/image/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamImage(@RequestParam String prompt) {
return Flux.create(sink -> {
ImageResponse res = imageModel.call(new ImagePrompt(prompt));
sink.next(ServerSentEvent.builder(res.getResult().getOutput().getUrl()).build());
sink.complete();
});
}
5.4 将图片保存到本地或 OSS
DashScope 返回的图片 URL 有有效期,过期就无法访问。你可以顺手把图片下载并持久化存储,比如保存到服务器本地或阿里云 OSS:
// 保存到本地
URL imageUrl = new URL(imageResponse.getResult().getOutput().getUrl());
BufferedImage image = ImageIO.read(imageUrl);
File outputFile = new File("/data/images/generated.png");
ImageIO.write(image, "png", outputFile);
生产环境建议上传到 OSS 并返回新的永久 URL。
六、部署实操:让服务跑起来
现在我们把服务搬到生产环境。这里提供从本地运行到 Docker 部署的完整步骤。
6.1 本地开发运行
进入项目目录(例如 dashscope-image),先确保环境变量已设置,然后:
mvn spring-boot:run
看到 Started DashScopeImageApplication in ... seconds 就说明启动成功。
快速测试:
# 生成一张图片并保存
curl http://localhost:10008/example/image -o test.png
6.2 打包为可执行 JAR
mvn clean package -DskipTests
在 target/ 目录下会生成 dashscope-image-1.0.0.jar(名字可能略有不同)。这个 JAR 内嵌了 Tomcat,可以直接运行:
export AI_DASHSCOPE_API_KEY=sk-xxx
java -jar target/dashscope-image-1.0.0.jar
6.3 部署到 Linux 服务器
把 JAR 上传到服务器的 /opt/dashscope-image/ 目录,然后创建 Systemd 服务来管理:
sudo tee /etc/systemd/system/dashscope-image.service > /dev/null <<EOF
[Unit]
Description=Spring AI DashScope Image Service
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/dashscope-image
ExecStart=/usr/bin/java -jar /opt/dashscope-image/dashscope-image-1.0.0.jar
Environment="AI_DASHSCOPE_API_KEY=sk-你的Key"
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
然后启动并设置开机自启:
sudo systemctl daemon-reload
sudo systemctl start dashscope-image
sudo systemctl enable dashscope-image
6.4 Docker 部署
如果喜欢用容器,可以这么玩。先在项目根目录创建 Dockerfile:
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/*.jar app.jar
ENV AI_DASHSCOPE_API_KEY=""
EXPOSE 10008
ENTRYPOINT ["java", "-jar", "app.jar"]
构建并运行:
docker build -t dashscope-image:latest .
docker run -d --name dashscope-image -p 10008:10008 \
-e AI_DASHSCOPE_API_KEY=sk-你的Key \
dashscope-image:latest
6.5 Nginx 反向代理 + HTTPS
为服务添加域名和 HTTPS 更安全。配置 Nginx 反向代理:
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://127.0.0.1:10008;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
然后使用 Let’s Encrypt 免费申请证书:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d your-domain.com
6.6 一键部署脚本
我还为你准备了一个快速部署脚本,把上面的手工操作自动化:
#!/bin/bash
JAR_FILE="dashscope-image-1.0.0.jar"
SERVICE_NAME="dashscope-image"
PORT=10008
API_KEY="sk-你的Key" # 实际使用时请用环境变量或参数传入
# 安装 Java(如未安装)
if ! command -v java &> /dev/null; then
sudo apt update && sudo apt install -y openjdk-21-jre
fi
# 准备目录
sudo mkdir -p /opt/$SERVICE_NAME
sudo cp $JAR_FILE /opt/$SERVICE_NAME/
# 创建 systemd 服务
sudo tee /etc/systemd/system/$SERVICE_NAME.service > /dev/null <<EOF
[Unit]
Description=Spring AI DashScope Image Service
After=network.target
[Service]
Type=simple
User=ubuntu
WorkingDirectory=/opt/$SERVICE_NAME
ExecStart=/usr/bin/java -jar /opt/$SERVICE_NAME/$JAR_FILE
Environment="AI_DASHSCOPE_API_KEY=$API_KEY"
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable $SERVICE_NAME --now
echo "✅ 服务已启动,访问 http://<服务器IP>:$PORT/example/image"
七、常见问题排查
Q1:启动报错“API Key 为空”
先检查环境变量是否设置:echo $AI_DASHSCOPE_API_KEY。如果没有,重新设置后重启应用。记得每次新建终端都要重新设置,或写入 ~/.bashrc。
Q2:接口返回的图片 URL 打不开
DashScope 返回的链接有效期较短,可能是几个小时。建议将图片下载保存到本地或对象存储。
Q3:批量生成数量超过 4 张报错
这是阿里云的限制,目前一次请求最多生成 1-4 张。需要更多可以循环调用。
Q4:外网无法访问
检查服务器安全组/防火墙是否放行了 10008 端口(sudo ufw allow 10008),以及 Nginx 是否正常运行。
Q5:生成失败返回“敏感内容”
调整提示词,避免过于露骨或违规描述。也可以联系阿里云提交审核豁免。
八、总结与下一步
恭喜你,完成了从理解原理、动手实践到生产部署的全过程!我们一起掌握了:
- 如何用
ImageModel和ImagePrompt抽象图像生成任务 - 如何构建三种不同复杂度的生成接口
- 如何优化大图传输、统一错误处理、扩展异步流式
- 如何把应用部署到 Linux 或 Docker 环境
接下来,你可以玩这些花样:
- 写个前端页面,让用户拖拽输入提示词,直接看到生成的图片。
- 对接自己的业务,例如电商自动生成商品宣传图,或者游戏生成角色概念图。
- 尝试其他模型,把 DashScope 换成 OpenAI 或 Stability AI,代码几乎不用改。
- 加入监控和日志,使用 Micrometer + Prometheus 观察接口耗时和成功率。
整个示例就像一个乐高模块,希望你能把它嵌入到更大的系统里,创造出更有意思的 AI 应用!如果遇到困难,记得回来看这篇博文 😄
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)