feat: use image URLs instead of Base64 for canvas image operations

- Modified ImageNode to upload images to storage bucket before use
- Updated RMBG API to support imageUrl parameter (fetch from URL and convert to Base64 internally)
- Updated Enhance API to support imageUrl parameter
- Updated canvas service layer to use imageUrl for all image processing APIs
- All processed images are saved to storage bucket and user gallery
This commit is contained in:
Your Name 2026-01-24 20:58:15 +08:00
parent bc7e967aba
commit 6a2cbb1b49
4 changed files with 159 additions and 28 deletions

View File

@ -366,7 +366,7 @@ export async function POST(request: NextRequest) {
}
}
// 2. 获取请求体(Base64图片或FormData)
// 2. 获取请求体(支持 imageUrl、image_base64 或 FormData)
console.log("[Enhance API] 开始获取请求体...");
const contentType = request.headers.get("content-type") || "";
console.log("[Enhance API] Content-Type:", contentType);
@ -377,18 +377,41 @@ export async function POST(request: NextRequest) {
let messageId: string | null = null; // 消息ID(可选)
if (contentType.includes("application/json")) {
// JSON格式:Base64图片
console.log("[Enhance API] 解析JSON请求体...");
const body = await request.json();
console.log("[Enhance API] JSON请求体解析完成,image_base64长度:", body.image_base64?.length || 0);
imageBase64 = body.image_base64;
prompt = body.prompt || "图片变清晰处理";
chatId = body.chatId || null;
messageId = body.messageId || null;
if (!imageBase64) {
// 优先使用 imageUrl,其次使用 image_base64
if (body.imageUrl) {
// 从 URL 获取图片并转换为 Base64
console.log(`[Enhance API] 从 URL 获取图片: ${body.imageUrl}`);
try {
const imageResponse = await fetch(body.imageUrl);
if (!imageResponse.ok) {
return NextResponse.json(
{ error: `无法获取图片: ${imageResponse.status} ${imageResponse.statusText}` },
{ status: 400 }
);
}
const arrayBuffer = await imageResponse.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
imageBase64 = buffer.toString("base64");
console.log(`[Enhance API] 图片已转换为 Base64,大小: ${buffer.length} bytes`);
} catch (fetchError) {
console.error("[Enhance API] 获取图片失败:", fetchError);
return NextResponse.json(
{ error: "无法获取图片,请检查图片链接是否有效" },
{ status: 400 }
);
}
} else if (body.image_base64) {
console.log("[Enhance API] JSON请求体解析完成,image_base64长度:", body.image_base64?.length || 0);
imageBase64 = body.image_base64;
} else {
return NextResponse.json(
{ error: "缺少image_base64参数" },
{ error: "缺少 imageUrl 或 image_base64 参数" },
{ status: 400 }
);
}

View File

@ -120,7 +120,7 @@ export async function POST(request: NextRequest) {
);
}
// 2. 获取请求体(Base64图片或FormData)
// 2. 获取请求体(支持 imageUrl、image_base64 或 FormData)
const contentType = request.headers.get("content-type") || "";
let imageBase64: string;
@ -129,16 +129,39 @@ export async function POST(request: NextRequest) {
let messageId: string | null = null; // 消息ID(可选)
if (contentType.includes("application/json")) {
// JSON格式:Base64图片
const body = await request.json();
imageBase64 = body.image_base64;
prompt = body.prompt || "抠图处理";
chatId = body.chatId || null;
messageId = body.messageId || null;
if (!imageBase64) {
// 优先使用 imageUrl,其次使用 image_base64
if (body.imageUrl) {
// 从 URL 获取图片并转换为 Base64
console.log(`[RMBG] 从 URL 获取图片: ${body.imageUrl}`);
try {
const imageResponse = await fetch(body.imageUrl);
if (!imageResponse.ok) {
return NextResponse.json(
{ error: `无法获取图片: ${imageResponse.status} ${imageResponse.statusText}` },
{ status: 400 }
);
}
const arrayBuffer = await imageResponse.arrayBuffer();
const buffer = Buffer.from(arrayBuffer);
imageBase64 = buffer.toString("base64");
console.log(`[RMBG] 图片已转换为 Base64,大小: ${buffer.length} bytes`);
} catch (fetchError) {
console.error("[RMBG] 获取图片失败:", fetchError);
return NextResponse.json(
{ error: "无法获取图片,请检查图片链接是否有效" },
{ status: 400 }
);
}
} else if (body.image_base64) {
imageBase64 = body.image_base64;
} else {
return NextResponse.json(
{ error: "缺少image_base64参数" },
{ error: "缺少 imageUrl 或 image_base64 参数" },
{ status: 400 }
);
}

View File

@ -34,19 +34,62 @@ export function ImageNode({ node, isSelected }: ImageNodeProps) {
const deleteNode = useCanvasStore((state) => state.deleteNode);
const addNode = useCanvasStore((state) => state.addNode);
// 处理图片上传
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
const [isUploading, setIsUploading] = useState(false);
// 处理图片上传 - 上传到存储桶,不使用 Base64
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
const src = event.target?.result as string;
// 验证文件类型
if (!file.type.startsWith('image/')) {
toast.error('请上传图片文件');
return;
}
// 验证文件大小 (8MB)
if (file.size > 8 * 1024 * 1024) {
toast.error('图片大小不能超过 8MB');
return;
}
setIsUploading(true);
// 先显示上传中状态
updateNode(node.id, {
data: { ...data, status: 'generating' },
});
try {
// 上传到存储桶
const result = await imageGenerationService.uploadImage(file);
if (result.success && result.data?.url) {
updateNode(node.id, {
data: {
...data,
src: result.data.url,
status: 'completed'
},
});
toast.success('图片上传成功');
} else {
updateNode(node.id, {
data: { ...data, status: 'failed' },
});
toast.error(result.error || '图片上传失败');
}
} catch (error) {
console.error('图片上传失败:', error);
updateNode(node.id, {
data: { ...data, src, status: 'completed' },
data: { ...data, status: 'failed' },
});
};
reader.readAsDataURL(file);
toast.error('图片上传失败,请重试');
} finally {
setIsUploading(false);
}
// 重置 input
e.target.value = '';
};
// 处理图片编辑操作 - 集成我们现有的 API

View File

@ -108,6 +108,36 @@ export const canvasService = {
// 图片生成服务
export const imageGenerationService = {
// 上传图片到存储桶
async uploadImage(file: File): Promise<ApiResponse<{ url: string; pathname: string }>> {
try {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/api/files/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const errorData = await response.json();
return { success: false, error: errorData.error || '上传失败' };
}
const data = await response.json();
return {
success: true,
data: {
url: data.url,
pathname: data.pathname
}
};
} catch (error) {
console.error('[Canvas] 图片上传失败:', error);
return { success: false, error: '图片上传失败' };
}
},
// AI 生成图片
async generateImage(params: {
prompt: string;
@ -130,47 +160,59 @@ export const imageGenerationService = {
}
},
// 图片抠图
// 图片抠图 - 使用图片URL
async removeBackground(imageUrl: string): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/rmbg', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl }),
body: JSON.stringify({ imageUrl }), // 使用 imageUrl 参数
});
const data = await response.json();
return data;
if (data.success && data.original_url) {
return { success: true, data: { url: data.original_url } };
}
return { success: false, error: data.error || '抠图失败' };
} catch (error) {
console.error('[Canvas] 抠图失败:', error);
return { success: false, error: '抠图失败' };
}
},
// 图片增强
// 图片增强 - 使用图片URL
async enhanceImage(imageUrl: string): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/enhance', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl }),
body: JSON.stringify({ imageUrl }), // 使用 imageUrl 参数
});
const data = await response.json();
return data;
if (data.success && data.original_url) {
return { success: true, data: { url: data.original_url } };
}
return { success: false, error: data.error || '图片增强失败' };
} catch (error) {
console.error('[Canvas] 图片增强失败:', error);
return { success: false, error: '图片增强失败' };
}
},
// 图片扩展
// 图片扩展 - 使用图片URL
async extendImage(imageUrl: string, direction?: string): Promise<ApiResponse<{ url: string }>> {
try {
const response = await fetch('/api/extend', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ imageUrl, direction }),
body: JSON.stringify({ image_url: imageUrl, direction }), // extend API 使用 image_url 参数名
});
const data = await response.json();
return data;
if (data.success && data.original_url) {
return { success: true, data: { url: data.original_url } };
}
return { success: false, error: data.error || '图片扩展失败' };
} catch (error) {
console.error('[Canvas] 图片扩展失败:', error);
return { success: false, error: '图片扩展失败' };
}
},