import type { NextApiRequest, NextApiResponse } from "next"; import sharp from "sharp"; import { authorizeDocuments, documentApiError } from "@/lib/documents-api"; import { saveDocumentImage } from "@/lib/document-images"; const maxUploadBytes = 8 * 1024 * 1024; const maxStoredBytes = 2 * 1024 * 1024; const imageTypes = new Set(["image/png", "image/jpeg", "image/webp"]); const imageFormats = new Set(["png", "jpeg", "webp"]); export const config = { api: { bodyParser: false } }; export default async function handler(request: NextApiRequest, response: NextApiResponse) { try { if (request.method !== "POST") { response.setHeader("Allow", "POST"); return response.status(405).end(); } if (!authorizeDocuments(request, response, true)) return; const type = request.headers["content-type"]?.split(";")[0].trim().toLowerCase(); if (!type || !imageTypes.has(type)) { return response.status(415).json({ error: "请上传 PNG、JPEG 或 WebP 图片" }); } if (Number(request.headers["content-length"]) > maxUploadBytes) { return response.status(413).json({ error: "图片上传上限为 8 MiB" }); } const chunks: Buffer[] = []; let length = 0; for await (const chunk of request.iterator({ destroyOnReturn: false })) { const bytes = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); length += bytes.length; if (length > maxUploadBytes) { return response.status(413).json({ error: "图片上传上限为 8 MiB" }); } chunks.push(bytes); } let encoded: Buffer; try { const input = sharp(Buffer.concat(chunks), { limitInputPixels: 25_000_000 }); const { format, pages } = await input.metadata(); if (!format || !imageFormats.has(format) || type !== `image/${format}` || (pages ?? 1) > 1) { return response.status(415).json({ error: "图片格式需与声明一致,且只支持静态图片" }); } encoded = await input.rotate() .resize({ width: 2000, height: 4000, fit: "inside", withoutEnlargement: true }) .webp({ quality: 85 }) .toBuffer(); } catch { return response.status(400).json({ error: "无法解码图片,或像素数量超过限制" }); } if (encoded.length > maxStoredBytes) { return response.status(413).json({ error: "处理后的图片超过 2 MiB,请缩小后重试" }); } const id = saveDocumentImage(encoded); const url = `/api/documents/images/${id}`; return response.status(200).json({ id, url, contentType: "image/webp", size: encoded.length }); } catch (error) { return documentApiError(response, error); } }