AI图片生成器:前端实现与后端API对接详解
本文将详细介绍一个完整的AI图片生成器项目,包含美观的前端界面和Spring Boot后端API实现。通过这个项目,您可以学习如何构建一个功能完整的AI图像生成应用。
项目概述
该AI图片生成器具有以下核心功能:
- 选择不同的AI模型进行图片生成
- 自定义图片尺寸、提示词、负向提示词等参数
- 实时显示生成进度和状态
- 支持暗夜/明亮模式切换
- 图片放大查看和下载功能
前端实现详解
1. 界面布局与主题切换
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AI 图片生成器</title>
<style>
:root {
–bg-color: #f5f7fa;
–text-color: #333;
–panel-bg: #ffffff;
–border-color: #e1e4e8;
–primary-color: #4a6bdf;
–secondary-color: #f0f2f5;
–button-hover: #3a5bd9;
–input-bg: #ffffff;
–progress-bg: #e0e0e0;
–image-bg: #f0f0f0;
–box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
–transition: all 0.3s ease;
}
.dark-mode {
–bg-color: #1a1a1a;
–text-color: #e0e0e0;
–panel-bg: #2d2d2d;
–border-color: #444;
–primary-color: #5d7eff;
–secondary-color: #3a3a3a;
–button-hover: #4a6bdf;
–input-bg: #3a3a3a;
–progress-bg: #444;
–image-bg: #2a2a2a;
–box-shadow: 0 4px 6px rgba(0, 0, 0, 0.3);
}
/* 其他样式省略… */
</style>
</head>
<body>
<div class="container">
<header>
<h1>AI 图片生成器</h1>
<button class="theme-toggle" id="themeToggle">
<span id="themeIcon">🌙</span> <span id="themeText">暗夜模式</span>
</button>
</header>
<div class="main-content">
<!– 参数面板 –>
<div class="panel params-panel">
<!– 参数设置区域 –>
</div>
<!– 预览面板 –>
<div class="panel preview-panel">
<!– 图片预览区域 –>
</div>
</div>
</div>
2. 参数设置区域
<div class="panel params-panel">
<h2 class="panel-title">生成参数</h2>
<div class="action-buttons">
<button id="generateBtn">生成图片</button>
<button id="downloadBtn" class="secondary-button" disabled>下载图片</button>
</div>
<div class="status-area" id="statusArea">就绪</div>
<div class="time-info" id="timeInfo"></div>
<div class="form-group">
<label for="model">模型选择</label>
<select id="model">
<option value="Qwen/Qwen-Image">Qwen/Qwen-Image</option>
<option value="Liudef/XB_F.1_MIX">Liudef/XB_F.1_MIX</option>
<option value="Liudef/XB_PONY">Liudef/XB_PONY</option>
<option value="Liudef/XB_Illustrious">Liudef/XB_Illustrious</option>
</select>
</div>
<div class="form-group">
<label for="prompt">正向提示词</label>
<textarea id="prompt" placeholder="请输入详细的描述词以获得更好的效果…">一只棕色的猫</textarea>
</div>
<div class="form-group">
<label for="negativePrompt">负向提示词 (可选)</label>
<textarea id="negativePrompt" placeholder="请输入不希望出现在图片中的内容…">lowres, bad anatomy, bad hands, text, error, missing fingers, extra digit, fewer digits, cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry</textarea>
</div>
<div class="advanced-params">
<h3 class="panel-title">高级参数</h3>
<div class="param-row">
<div class="param-item">
<label for="width">宽度</label>
<input type="number" id="width" min="64" max="2048" step="64" value="1024">
</div>
<div class="param-item">
<label for="height">高度</label>
<input type="number" id="height" min="64" max="2048" step="64" value="1024">
</div>
</div>
<div class="param-row">
<div class="param-item">
<label for="steps">采样步数</label>
<input type="number" id="steps" min="1" max="100" value="30">
</div>
<div class="param-item">
<label for="guidance">引导系数</label>
<input type="number" id="guidance" min="1.5" max="20" step="0.1" value="3.5">
</div>
</div>
</div>
<div class="progress-container">
<progress id="progressBar" value="0" max="100" style="display: none;"></progress>
<div class="loader" id="loader"></div>
</div>
</div>
3. 图片预览区域
<div class="panel preview-panel">
<h2 class="panel-title">图片预览</h2>
<div class="preview-container" id="previewContainer">
<img id="previewImage" class="preview-image" alt="生成的图片">
<div class="preview-placeholder" id="previewPlaceholder">
<p>图片将在此处显示</p>
<p>点击生成的图片可以放大查看</p>
</div>
</div>
</div>
<!– 图片放大模态框 –>
<div class="modal" id="imageModal">
<span class="close-modal" id="closeModal">×</span>
<img class="modal-content" id="modalImage">
</div>
4. JavaScript功能实现
// 主题切换
const themeToggle = document.getElementById('themeToggle');
const themeIcon = document.getElementById('themeIcon');
const themeText = document.getElementById('themeText');
// 检查本地存储中的主题偏好
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.body.classList.add('dark-mode');
themeIcon.textContent = '☀️';
themeText.textContent = '明亮模式';
}
themeToggle.addEventListener('click', () => {
document.body.classList.toggle('dark-mode');
if (document.body.classList.contains('dark-mode')) {
localStorage.setItem('theme', 'dark');
themeIcon.textContent = '☀️';
themeText.textContent = '明亮模式';
} else {
localStorage.setItem('theme', 'light');
themeIcon.textContent = '🌙';
themeText.textContent = '暗夜模式';
}
});
// DOM 元素
const generateBtn = document.getElementById('generateBtn');
const downloadBtn = document.getElementById('downloadBtn');
const progressBar = document.getElementById('progressBar');
const statusArea = document.getElementById('statusArea');
const timeInfo = document.getElementById('timeInfo');
const previewImage = document.getElementById('previewImage');
const previewPlaceholder = document.getElementById('previewPlaceholder');
const previewContainer = document.getElementById('previewContainer');
const loader = document.getElementById('loader');
// 图片放大相关元素
const imageModal = document.getElementById('imageModal');
const modalImage = document.getElementById('modalImage');
const closeModal = document.getElementById('closeModal');
// 图片放大功能
previewContainer.addEventListener('click', () => {
if (!previewImage.src) return;
modalImage.src = previewImage.src;
imageModal.classList.add('show');
});
closeModal.addEventListener('click', () => {
imageModal.classList.remove('show');
});
imageModal.addEventListener('click', (e) => {
if (e.target === imageModal) {
imageModal.classList.remove('show');
}
});
// 状态变量
let isGenerating = false;
let startTime = null;
let currentImageUrl = null;
let currentTaskId = null;
// 更新状态
function updateStatus(message, isError = false) {
statusArea.textContent = message;
statusArea.style.color = isError ? '#ff4d4f' : 'inherit';
statusArea.style.borderLeftColor = isError ? '#ff4d4f' : 'var(–primary-color)';
}
// 更新计时器
function updateTimer() {
if (!isGenerating || !startTime) return;
const elapsed = Math.floor((Date.now() – startTime) / 1000);
const minutes = Math.floor(elapsed / 60).toString().padStart(2, '0');
const seconds = (elapsed % 60).toString().padStart(2, '0');
timeInfo.textContent = `生成耗时: ${minutes}:${seconds}`;
if (isGenerating) {
requestAnimationFrame(updateTimer);
}
}
// 显示图片
function showImage(imageUrl) {
previewPlaceholder.style.display = 'none';
previewImage.style.display = 'block';
previewImage.src = imageUrl;
currentImageUrl = imageUrl;
downloadBtn.disabled = false;
// 添加图片加载动画
previewImage.style.opacity = '0';
setTimeout(() => {
previewImage.style.opacity = '1';
}, 10);
}
// 下载图片
downloadBtn.addEventListener('click', () => {
if (!currentImageUrl) return;
const link = document.createElement('a');
link.href = currentImageUrl;
link.download = `ai_image_${new Date().toISOString().slice(0, 10)}.jpg`;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
// 生成图片
generateBtn.addEventListener('click', async () => {
if (isGenerating) {
updateStatus('当前已有任务正在运行', true);
return;
}
const model = document.getElementById('model').value;
const prompt = document.getElementById('prompt').value.trim();
const negativePrompt = document.getElementById('negativePrompt').value.trim();
const width = parseInt(document.getElementById('width').value);
const height = parseInt(document.getElementById('height').value);
const steps = parseInt(document.getElementById('steps').value);
const guidance = parseFloat(document.getElementById('guidance').value);
// 验证输入
if (!prompt) {
updateStatus('请输入提示词', true);
return;
}
if (width < 64 || width > 2048 || height < 64 || height > 2048) {
updateStatus('图像尺寸必须在64-2048像素之间', true);
return;
}
if (steps < 1 || steps > 100) {
updateStatus('采样步数必须在1-100之间', true);
return;
}
if (guidance < 1.5 || guidance > 20) {
updateStatus('引导系数必须在1.5-20.0之间', true);
return;
}
// 开始生成
isGenerating = true;
startTime = Date.now();
generateBtn.disabled = true;
progressBar.style.display = 'none';
loader.style.display = 'block';
updateStatus('提交任务…');
updateTimer();
try {
// 提交任务到后端代理
const response = await fetch('/api/modelScope/generate', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
model: model,
prompt: prompt,
negativePrompt: negativePrompt || undefined,
width: width,
height: height,
steps: steps,
guidance: guidance
})
});
if (!response.ok) {
throw new Error(`API 错误: ${response.status} ${response.statusText}`);
}
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
currentTaskId = data.task_id;
updateStatus(`任务已提交 (ID: ${currentTaskId})`);
// 开始轮询任务状态
checkTaskStatus(currentTaskId);
} catch (error) {
handleGenerationError(error);
}
});
// 轮询任务状态
async function checkTaskStatus(taskId) {
try {
const response = await fetch(`/api/modelScope/checkStatus?taskId=${taskId}`);
if (!response.ok) {
throw new Error(`状态检查失败: ${response.status} ${response.statusText}`);
}
const data = await response.json();
if (data.error) {
throw new Error(data.error);
}
const status = data.task_status;
// 更新进度和状态
switch (status) {
case 'PENDING':
updateStatus('任务排队中…');
progressBar.value = 10;
break;
case 'PROCESSING':
updateStatus('图片生成中…');
progressBar.value = 30;
break;
case 'SUCCEED':
progressBar.value = 100;
updateStatus('生成成功!');
// 获取图片
const imageUrl = data.output_images[0];
showImage(imageUrl);
// 计算总耗时
const totalTime = Math.floor((Date.now() – startTime) / 1000);
const minutes = Math.floor(totalTime / 60);
const seconds = totalTime % 60;
timeInfo.textContent = `总耗时: ${minutes}分${seconds}秒`;
// 完成
finishGeneration();
return;
case 'FAILED':
throw new Error('图片生成失败: ' + (data.message || '未知错误'));
default:
// 继续轮询
break;
}
// 如果任务未完成,继续轮询
setTimeout(() => checkTaskStatus(taskId), 3000);
} catch (error) {
handleGenerationError(error);
}
}
// 处理生成错误
function handleGenerationError(error) {
updateStatus(`错误: ${error.message}`, true);
// 计算总耗时
if (startTime) {
const totalTime = Math.floor((Date.now() – startTime) / 1000);
const minutes = Math.floor(totalTime / 60);
const seconds = totalTime % 60;
timeInfo.textContent = `总耗时: ${minutes}分${seconds}秒`;
}
finishGeneration();
}
// 完成生成
function finishGeneration() {
isGenerating = false;
generateBtn.disabled = false;
progressBar.style.display = 'none';
loader.style.display = 'none';
currentTaskId = null;
}
后端API实现(Spring Boot)
package com.ai.controller;
import org.springframework.http.*;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/modelScope/")
public class AiModelScopeController {
// 配置您的API密钥
private static final String MODEL_SCOPE_API_KEY = "从平台获取key";
private static final String BASE_URL = "https://api-inference.modelscope.cn/";
@PostMapping("/generate")
public ResponseEntity<?> generateImage(@RequestBody Map<String, Object> requestData) {
try {
// 构建请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", requestData.get("model"));
requestBody.put("prompt", requestData.get("prompt"));
requestBody.put("negative_prompt", requestData.get("negativePrompt"));
requestBody.put("size", requestData.get("width") + "x" + requestData.get("height"));
requestBody.put("steps", requestData.get("steps"));
requestBody.put("guidance", requestData.get("guidance"));
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + MODEL_SCOPE_API_KEY);
headers.set("Content-Type", "application/json");
headers.set("X-ModelScope-Async-Mode", "true");
// 发送请求
RestTemplate restTemplate = new RestTemplate();
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(requestBody, headers);
ResponseEntity<Map> response = restTemplate.postForEntity(
BASE_URL + "v1/images/generations",
requestEntity,
Map.class);
return ResponseEntity.ok(response.getBody());
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", e.getMessage()));
}
}
@GetMapping("/checkStatus")
public ResponseEntity<?> checkStatus(@RequestParam String taskId) {
try {
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + MODEL_SCOPE_API_KEY);
headers.set("X-ModelScope-Task-Type", "image_generation");
// 发送请求
RestTemplate restTemplate = new RestTemplate();
HttpEntity<String> requestEntity = new HttpEntity<>(headers);
ResponseEntity<Map> response = restTemplate.exchange(
BASE_URL + "v1/tasks/" + taskId,
HttpMethod.GET,
requestEntity,
Map.class);
return ResponseEntity.ok(response.getBody());
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(Map.of("error", e.getMessage()));
}
}
}
项目部署指南
前端部署:
- 将HTML文件部署在任何静态Web服务器上(如Nginx、Apache)
- 或者直接在浏览器中打开HTML文件
后端部署:
- 使用Maven或Gradle构建Spring Boot项目
- 在application.properties中添加配置:server.port=8080
- 运行Spring Boot应用
API密钥配置:
- 从ModelScope平台获取API密钥
- 替换后端代码中的MODEL_SCOPE_API_KEY值
总结
本文详细介绍了一个完整的AI图片生成器实现方案,包括:
前端界面:
- 响应式设计,支持移动设备
- 暗夜/明亮模式切换
- 参数设置面板
- 图片预览与放大功能
- 生成状态和进度显示
后端API:
- 图片生成任务提交接口
- 任务状态查询接口
- 与ModelScope API的对接
核心功能:
- 多模型支持
- 自定义图片尺寸和生成参数
- 进度实时显示
- 图片下载功能
该项目的完整代码可以直接用于实际开发,只需替换后端的API密钥即可与ModelScope平台对接。项目结构清晰,代码可读性强,适合作为AI应用开发的入门项目。
官方api地址 模型下载地址 官网地址
评论前必须登录!
注册