import { NextRequest, NextResponse } from "next/server"; import { auth } from "@/app/(auth)/auth"; import { uploadFile } from "@/lib/storage/service"; import { db } from "@/lib/db"; import { generatedImage } from "@/lib/db/schema"; /** * 压缩图片生成缩略图(使用 sharp) */ async function createThumbnail(buffer: Buffer, mimeType: string): Promise { try { const sharp = await import("sharp"); // 创建缩略图(宽度 500px,高度按比例,质量 80%) const thumbnailBuffer = await sharp.default(buffer) .resize(500, null, { fit: "inside", withoutEnlargement: true, }) .png({ quality: 80 }) // PNG格式保持透明背景 .toBuffer(); console.log(`[Resize] Thumbnail created, size: ${thumbnailBuffer.length} bytes (original: ${buffer.length} bytes)`); return thumbnailBuffer; } catch (error) { console.error("[Resize] Failed to create thumbnail:", error); // 如果失败,返回原图 return buffer; } } /** * 将 base64 图片上传到 S3,返回缩略图和原图的 URL */ async function uploadBase64ToS3(base64Data: string, mimeType: string = "image/png"): Promise<{ thumbnail: string; original: string }> { try { // 解析 base64 数据 const buffer = Buffer.from(base64Data, "base64"); // 生成文件名 const ext = mimeType.split("/")[1] || "png"; const timestamp = Date.now(); const randomStr = Math.random().toString(36).slice(2, 8); const thumbnailFilename = `resize-thumb-${timestamp}-${randomStr}.${ext}`; const originalFilename = `resize-orig-${timestamp}-${randomStr}.${ext}`; console.log(`[Resize] Original size: ${buffer.length} bytes`); // 生成缩略图 const thumbnailBuffer = await createThumbnail(buffer, mimeType); // 转换为 ArrayBuffer const thumbnailArrayBuffer = thumbnailBuffer.buffer.slice( thumbnailBuffer.byteOffset, thumbnailBuffer.byteOffset + thumbnailBuffer.byteLength ) as ArrayBuffer; const originalArrayBuffer = buffer.buffer.slice( buffer.byteOffset, buffer.byteOffset + buffer.byteLength ) as ArrayBuffer; // 并行上传缩略图和原图到 S3 console.log(`[Resize] Uploading thumbnail and original to S3...`); const [thumbnailResult, originalResult] = await Promise.all([ uploadFile(thumbnailFilename, thumbnailArrayBuffer), uploadFile(originalFilename, originalArrayBuffer), ]); console.log(`[Resize] Uploaded to S3:`); console.log(` - Thumbnail: ${thumbnailResult.url}`); console.log(` - Original: ${originalResult.url}`); return { thumbnail: thumbnailResult.url, original: originalResult.url, }; } catch (error) { console.error("[Resize] Failed to upload to S3:", error); throw new Error(`图片上传到存储桶失败: ${error instanceof Error ? error.message : "未知错误"}。请检查系统设置中的存储配置。`); } } /** * 尺寸调整API路由 * 接收前端处理后的Base64图片,上传到S3并保存到数据库 */ export async function POST(request: NextRequest) { try { // 0. 获取用户会话 const session = await auth(); if (!session?.user?.id) { return NextResponse.json( { error: "未授权,请先登录" }, { status: 401 } ); } const userId = session.user.id; // 1. 获取请求体 const contentType = request.headers.get("content-type") || ""; let imageBase64: string; let prompt = "尺寸调整处理"; let chatId: string | null = null; let messageId: string | null = null; let canvasWidth: number; let canvasHeight: number; let mimeType = "image/png"; if (contentType.includes("application/json")) { const body = await request.json(); imageBase64 = body.image_base64; prompt = body.prompt || "尺寸调整处理"; chatId = body.chatId || null; messageId = body.messageId || null; canvasWidth = body.canvasWidth; canvasHeight = body.canvasHeight; mimeType = body.mimeType || "image/png"; if (!imageBase64) { return NextResponse.json( { error: "缺少image_base64参数" }, { status: 400 } ); } if (!canvasWidth || !canvasHeight) { return NextResponse.json( { error: "缺少canvasWidth或canvasHeight参数" }, { status: 400 } ); } } else { return NextResponse.json( { error: "不支持的Content-Type,请使用application/json" }, { status: 400 } ); } console.log(`[Resize API] 图片Base64长度: ${imageBase64.length} 字符`); console.log(`[Resize API] 画布尺寸: ${canvasWidth}x${canvasHeight}`); // 2. 上传处理后的图片到S3存储桶 let thumbnailUrl: string; let originalUrl: string; try { const uploadResult = await uploadBase64ToS3(imageBase64, mimeType); thumbnailUrl = uploadResult.thumbnail; originalUrl = uploadResult.original; console.log(`[Resize] ✅ Images uploaded to S3 successfully`); } catch (uploadError) { console.error("[Resize] ❌ Failed to upload to S3:", uploadError); return NextResponse.json( { error: "图片上传到存储桶失败", detail: uploadError instanceof Error ? uploadError.message : "未知错误" }, { status: 500 } ); } // 3. 保存图片记录到数据库(相册和生图记录) try { const [savedImage] = await db.insert(generatedImage).values({ userId, chatId: chatId || null, messageId: messageId || null, thumbnailUrl, originalUrl, imageSize: `${canvasWidth}x${canvasHeight}`, // 使用实际尺寸 prompt: prompt || "尺寸调整处理", modelId: "resize-1.0", // 标记为尺寸调整处理 }).returning(); console.log(`[Resize] ✅ 图片已保存到相册和生图记录,ID: ${savedImage.id}`); } catch (dbError) { console.error("[Resize] ❌ 保存到数据库失败:", dbError); // 数据库保存失败时仍然返回成功,但记录错误(图片已上传到S3,用户可以访问) } // 4. 返回结果(包含S3 URL和Base64,前端可以选择使用) return NextResponse.json({ success: true, image_base64: imageBase64, // 保留Base64用于前端立即显示 thumbnail_url: thumbnailUrl, // S3缩略图URL original_url: originalUrl, // S3原图URL canvas_width: canvasWidth, canvas_height: canvasHeight, }); } catch (error) { console.error("[Resize API] 处理错误:", error); return NextResponse.json( { error: "服务器内部错误", detail: error instanceof Error ? error.message : "未知错误" }, { status: 500 } ); } }