更新生成接口,支持上传参考图并使用 URL 代替 Base64 格式
This commit is contained in:
parent
efaa26314c
commit
bc05afbcb0
@ -1,5 +1,5 @@
|
||||
from flask import Blueprint, request, jsonify, session, current_app
|
||||
from urllib.parse import unquote, urlparse
|
||||
from urllib.parse import unquote, urljoin, urlparse
|
||||
from config import Config
|
||||
from extensions import db, redis_client, s3_client
|
||||
from models import User, SavedPrompt
|
||||
@ -72,6 +72,34 @@ def generate():
|
||||
user = db.session.get(User, user_id)
|
||||
data = request.json if request.is_json else request.form
|
||||
|
||||
image_urls = data.get(
|
||||
"image",
|
||||
data.get("image_urls", data.get("image_data", [])),
|
||||
) or []
|
||||
if isinstance(image_urls, str):
|
||||
image_urls = [image_urls]
|
||||
elif not isinstance(image_urls, (list, tuple)):
|
||||
return jsonify({"error": "参考图地址格式无效"}), 400
|
||||
|
||||
normalized_image_urls = []
|
||||
for image_url in image_urls:
|
||||
if not isinstance(image_url, str) or not image_url.strip():
|
||||
return jsonify({"error": "参考图地址无效"}), 400
|
||||
|
||||
image_url = image_url.strip()
|
||||
parsed_input = urlparse(image_url)
|
||||
if image_url.startswith("/"):
|
||||
absolute_url = urljoin(request.url_root, image_url)
|
||||
elif parsed_input.scheme in ("http", "https") and parsed_input.netloc:
|
||||
absolute_url = image_url
|
||||
else:
|
||||
return jsonify({"error": "参考图必须使用 HTTP(S) URL,不能使用 Base64"}), 400
|
||||
|
||||
parsed_url = urlparse(absolute_url)
|
||||
if parsed_url.scheme not in ("http", "https") or not parsed_url.netloc:
|
||||
return jsonify({"error": "参考图必须使用 HTTP(S) URL,不能使用 Base64"}), 400
|
||||
normalized_image_urls.append(absolute_url)
|
||||
|
||||
api_key, target_api, cost, use_trial, error = validate_generation_request(user, data)
|
||||
if error:
|
||||
return jsonify({"error": error}), 400
|
||||
@ -92,7 +120,6 @@ def generate():
|
||||
)
|
||||
return jsonify(result), status_code
|
||||
|
||||
image_data = data.get("image_data", [])
|
||||
if model_value == "gpt-image-2":
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
@ -101,8 +128,8 @@ def generate():
|
||||
"size": data.get("size") or "2048x2048",
|
||||
"quality": "high",
|
||||
}
|
||||
if image_data:
|
||||
payload["image"] = [img.split(",", 1)[1] if "," in img else img for img in image_data]
|
||||
if normalized_image_urls:
|
||||
payload["image"] = normalized_image_urls
|
||||
else:
|
||||
payload = {
|
||||
"prompt": prompt,
|
||||
@ -110,8 +137,8 @@ def generate():
|
||||
"response_format": "url",
|
||||
"aspect_ratio": data.get("ratio"),
|
||||
}
|
||||
if image_data:
|
||||
payload["image"] = [img.split(",", 1)[1] if "," in img else img for img in image_data]
|
||||
if normalized_image_urls:
|
||||
payload["image"] = normalized_image_urls
|
||||
|
||||
if model_value in ("nano-banana-2", "gemini-3.1-flash-image-preview") and data.get("size"):
|
||||
payload["image_size"] = data.get("size")
|
||||
|
||||
@ -713,53 +713,27 @@ document.getElementById('submitBtn').onclick = async () => {
|
||||
currentGeneratedUrls = []; // 重置当前生成列表
|
||||
|
||||
try {
|
||||
let image_data = [];
|
||||
let image = [];
|
||||
|
||||
// 1. 将图片转换为 Base64 (并压缩过大图片)
|
||||
// 1. 上传参考图,生图接口统一使用 URL
|
||||
if (uploadedFiles.length > 0) {
|
||||
btnText.innerText = "正在准备图片数据...";
|
||||
const processImageFile = (file) => new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const img = new Image();
|
||||
img.onload = () => {
|
||||
const maxDim = 2048;
|
||||
let w = img.width;
|
||||
let h = img.height;
|
||||
btnText.innerText = "正在上传参考图...";
|
||||
const formData = new FormData();
|
||||
uploadedFiles.forEach(file => formData.append('images', file));
|
||||
|
||||
if (w <= maxDim && h <= maxDim) {
|
||||
resolve(e.target.result);
|
||||
return;
|
||||
}
|
||||
|
||||
if (w > h) {
|
||||
if (w > maxDim) {
|
||||
h = Math.round(h * (maxDim / w));
|
||||
w = maxDim;
|
||||
}
|
||||
} else {
|
||||
if (h > maxDim) {
|
||||
w = Math.round(w * (maxDim / h));
|
||||
h = maxDim;
|
||||
}
|
||||
}
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0, w, h);
|
||||
// 保持原格式,如果不是 png 则默认 jpeg (0.9 质量)
|
||||
const outType = file.type === 'image/png' ? 'image/png' : 'image/jpeg';
|
||||
resolve(canvas.toDataURL(outType, 0.9));
|
||||
};
|
||||
img.onerror = reject;
|
||||
img.src = e.target.result;
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsDataURL(file);
|
||||
const uploadResponse = await fetch('/api/upload', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
image_data = await Promise.all(uploadedFiles.map(f => processImageFile(f)));
|
||||
const uploadResult = await uploadResponse.json();
|
||||
if (!uploadResponse.ok || uploadResult.error) {
|
||||
throw new Error(uploadResult.error || '参考图上传失败');
|
||||
}
|
||||
|
||||
image = (uploadResult.urls || []).map(url => new URL(url, window.location.origin).href);
|
||||
if (image.length !== uploadedFiles.length) {
|
||||
throw new Error('部分参考图上传失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 并行启动多个生成任务
|
||||
@ -785,7 +759,7 @@ document.getElementById('submitBtn').onclick = async () => {
|
||||
model: document.getElementById('modelSelect').value,
|
||||
ratio: document.getElementById('modelSelect').value === GPT_IMAGE_EDIT_MODEL ? undefined : document.getElementById('ratioSelect').value,
|
||||
size: document.getElementById('sizeSelect').value,
|
||||
image_data // 发送 Base64 数组
|
||||
image
|
||||
})
|
||||
});
|
||||
const res = await r.json();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user