Files
smalltown/app/api/upload/route.ts
2026-03-31 00:03:08 +08:00

95 lines
3.0 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextRequest, NextResponse } from 'next/server';
import fs from 'fs/promises';
import path from 'path';
import sharp from 'sharp';
const UPLOAD_DIR = path.join(process.cwd(), 'public/uploads');
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
export async function POST(request: NextRequest) {
try {
const formData = await request.formData();
const file = formData.get('file') as File | null;
if (!file) {
return NextResponse.json({ error: '请选择图片' }, { status: 400 });
}
const allowedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!allowedTypes.includes(file.type)) {
return NextResponse.json({ error: '仅支持 JPG、PNG、GIF、WebP 格式' }, { status: 400 });
}
const ext = 'jpg';
const filename = `${Date.now()}-${Math.random().toString(36).slice(2)}.${ext}`;
const filepath = path.join(UPLOAD_DIR, filename);
const buffer = await file.arrayBuffer();
let imageBuffer = Buffer.from(buffer);
// 获取图片尺寸
const image = sharp(imageBuffer);
const metadata = await image.metadata();
// 如果超过5MB进行压缩
if (imageBuffer.length > MAX_SIZE) {
let quality = 85;
// 先尝试缩小尺寸
if (metadata.width && metadata.width > 1920) {
imageBuffer = await image
.resize(1920, null, { withoutEnlargement: true })
.toBuffer() as any;
}
// 循环压缩直到小于5MB
while (imageBuffer.length > MAX_SIZE && quality > 30) {
imageBuffer = await sharp(imageBuffer)
.jpeg({ quality, progressive: true })
.toBuffer() as any;
quality -= 10;
}
// 如果还是太大,继续缩小尺寸
if (imageBuffer.length > MAX_SIZE && metadata.width && metadata.width > 800) {
imageBuffer = await sharp(imageBuffer)
.resize(800, null, { withoutEnlargement: true })
.jpeg({ quality: 70, progressive: true })
.toBuffer() as any;
}
} else {
// 转换为jpeg并优化
imageBuffer = await sharp(imageBuffer)
.jpeg({ quality: 85, progressive: true })
.toBuffer() as any;
}
await fs.writeFile(filepath, imageBuffer);
// 返回相对路径
const url = `/uploads/${filename}`;
return NextResponse.json({ url });
} catch (error) {
console.error('Upload error:', error);
return NextResponse.json({ error: '上传失败' }, { status: 500 });
}
}
export async function DELETE(request: NextRequest) {
try {
const { searchParams } = new URL(request.url);
const filename = searchParams.get('filename');
if (!filename) {
return NextResponse.json({ error: '缺少文件名' }, { status: 400 });
}
const filepath = path.join(UPLOAD_DIR, path.basename(filename));
await fs.unlink(filepath);
return NextResponse.json({ success: true });
} catch (error) {
console.error('Delete error:', error);
return NextResponse.json({ error: '删除失败' }, { status: 500 });
}
}