黄俪在设计课程管理后台时抱怨:"每门课都要配封面图,找素材太费时间了。"白歌指了指屏幕:"让 AI 画——ImageModel 一行代码出图。"
图像生成
定义与作用
Spring AI 的 ImageModel 接口封装了 AI 图像生成能力——通过文本描述(Prompt)生成图片。与视觉模型("看图说话")方向相反,图像生成是"依话画图"。
核心原理
图释:ImageModel 接收文本描述,调用图像生成 API(如 OpenAI DALL-E),返回 ImageResponse 包含生成的图片数据,应用可将其保存到本地文件系统。
完整示例一:生成课程封面图
场景说明
黄俪需要为飞翔大学课程管理系统自动生成课程封面图。
依赖引入与配置
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
image:
options:
model: dall-e-3
size: 1024x1024
quality: standard
关键代码
@Service
public class CourseCoverService {
private final ImageModel imageModel;
public CourseCoverService(ImageModel imageModel) {
this.imageModel = imageModel;
}
public String generateAndSave(String courseName, String outputPath)
throws IOException {
// 构建生成提示词
ImagePrompt prompt = new ImagePrompt(
String.format("""
为课程"%s"设计一张现代简约风格的封面图。
要求:教育科技主题,蓝白配色,包含抽象的算法/数据结构图案,
不要出现文字。适合用于在线教育平台展示。
""", courseName),
ImageOptions.builder()
.model("dall-e-3")
.quality("standard")
.N(1)
.height(1024)
.width(1024)
.build()
);
// 调用图像生成
ImageResponse response = imageModel.call(prompt);
Image image = response.getResult().getOutput();
// 保存到本地
String url = image.getUrl(); // 或 image.getB64Json() 获取 Base64
if (url != null) {
downloadImage(url, outputPath);
} else {
saveBase64Image(image.getB64Json(), outputPath);
}
return outputPath;
}
private void downloadImage(String url, String path) throws IOException {
try (InputStream in = new URL(url).openStream()) {
Files.copy(in, Path.of(path), StandardCopyOption.REPLACE_EXISTING);
}
}
private void saveBase64Image(String b64Json, String path)
throws IOException {
byte[] data = Base64.getDecoder().decode(b64Json);
Files.write(Path.of(path), data);
}
}
@RestController
public class CoverController {
private final CourseCoverService coverService;
public CoverController(CourseCoverService coverService) {
this.coverService = coverService;
}
@PostMapping("/course/cover")
public Map<String, String> generateCover(@RequestParam String courseName)
throws IOException {
String fileName = courseName.replaceAll("\\s+", "_") + "_cover.png";
String outputPath = "C:\\Users\\AOXIANG\\Desktop\\SpringAI\\covers\\"
+ fileName;
Files.createDirectories(Path.of(outputPath).getParent());
String savedPath = coverService.generateAndSave(courseName, outputPath);
return Map.of("course", courseName, "coverPath", savedPath);
}
}
运行结果
POST /course/cover?courseName=数据结构与算法
{
"course": "数据结构与算法",
"coverPath": "C:\\Users\\AOXIANG\\Desktop\\SpringAI\\covers\\数据结构与算法_cover.png"
}
控制台日志:
[main] INFO c.f.s.CourseCoverService - 调用 DALL-E 3 生成课程封面...
[main] INFO c.f.s.CourseCoverService - 图片已保存:1024×1024,文件大小 2.3MB
完整示例二:批量生成多尺寸缩略图
场景说明
杨英要做活动推广,需要为同一门课程生成多种尺寸的推广图。
关键代码
@Service
public class ThumbnailService {
private final ImageModel imageModel;
public ThumbnailService(ImageModel imageModel) {
this.imageModel = imageModel;
}
public Map<String, String> generateThumbnails(
String courseName, String outputDir) throws IOException {
Map<String, String> results = new HashMap<>();
// 尺寸 1:大图 1024×1024
results.put("large", generate(courseName, outputDir, "large",
1024, 1024));
// 尺寸 2:小图 256×256
results.put("small", generate(courseName, outputDir, "small",
256, 256));
return results;
}
private String generate(String courseName, String dir,
String sizeLabel, int w, int h) throws IOException {
ImagePrompt prompt = new ImagePrompt(
String.format("课程封面:%s,简洁风格", courseName),
ImageOptions.builder()
.model("dall-e-2") // dall-e-2 便宜且够用
.N(1)
.height(h)
.width(w)
.build()
);
ImageResponse response = imageModel.call(prompt);
String url = response.getResult().getOutput().getUrl();
String path = dir + "\\" + courseName + "_" + sizeLabel + ".png";
try (InputStream in = new URL(url).openStream()) {
Files.copy(in, Path.of(path), StandardCopyOption.REPLACE_EXISTING);
}
return path;
}
}
运行结果
POST /course/thumbnails?courseName=线性代数
{
"large": ".../covers/线性代数_large.png (1024×1024, 2.3MB)",
"small": ".../covers/线性代数_small.png (256×256, 48KB)"
}
易错场景与面试考点
易错场景一:混淆 ImageModel 和 Vision Model
// ❌ 错误:想用 ChatClient 生成图片
chatClient.prompt()
.user("画一张课程封面")
.call()
.content(); // 返回文本描述,不是图片
问题分析
ChatClient 返回的是文本,ImageModel 返回的是图片数据。两者方向相反。
// ✅ 正确:使用 ImageModel 生成图片
ImagePrompt prompt = new ImagePrompt("课程封面:数据结构");
ImageResponse response = imageModel.call(prompt);
// 从 response 获取图片 URL 或 Base64 数据
面试高频题
Q1:ImageModel 和 ChatClient 的视觉输入有什么区别?
ImageModel 是"文本 → 图片"(生成式),ChatClient + Media 是"图片 → 文本"(理解式)。前者创建新图片,后者分析已有图片。两者在 Spring AI 中使用不同的接口。
Q2:如何降低图像生成的 API 成本?
①小图优先用 dall-e-2(约 $0.02/张),大图用 dall-e-3(约 $0.04/张);②合理设置尺寸(256×256 比 1024×1024 便宜很多);③缓存已生成的封面图,避免重复调用。④本地环境可使用 Stable Diffusion 等开源模型完全免费。
本章小结
- ImageModel 接口实现"文本 → 图片"的 AI 生成
- ImagePrompt + ImageOptions 控制生成参数(模型、尺寸、质量)
- 返回的 ImageResponse 可通过 URL 或 Base64 获取图片数据
- 与视觉模型方向相反:"看图说话"用 ChatClient + Media,"依话画图"用 ImageModel