diff --git a/PROJECT.md b/PROJECT.md index 7e52c64..e2758de 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -141,16 +141,11 @@ npm run dev ```bash npm run build npm start +# 默认端口3000,可通过 PORT=3001 npm start 指定端口 ``` -### 8.3 远程访问 -SSH反向隧道将远程30000端口映射到本地3000: -```bash -./port_forwarding.sh start # 启动 -./port_forwarding.sh stop # 停止 -./port_forwarding.sh status # 状态 -# 访问 http://47.120.74.73:30000 -``` +### 8.3 图片服务(规划中) +计划将图片上传独立出来,新建 `smalltown-upload` 项目处理图片存储和压缩。 ## 九、注意事项 diff --git a/app/api/upload/route.ts b/app/api/upload/route.ts index 4ccfa85..f5a916a 100644 --- a/app/api/upload/route.ts +++ b/app/api/upload/route.ts @@ -1,12 +1,13 @@ 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 +const OSS_URL = 'http://localhost:9000'; +const API_KEY = '7cf93760ea49b750c96e6078b364e5f0'; export async function POST(request: NextRequest) { + let imageBuffer: Buffer; + try { const formData = await request.formData(); const file = formData.get('file') as File | null; @@ -20,29 +21,22 @@ export async function POST(request: NextRequest) { 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); + 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 }) @@ -50,7 +44,6 @@ export async function POST(request: NextRequest) { quality -= 10; } - // 如果还是太大,继续缩小尺寸 if (imageBuffer.length > MAX_SIZE && metadata.width && metadata.width > 800) { imageBuffer = await sharp(imageBuffer) .resize(800, null, { withoutEnlargement: true }) @@ -58,17 +51,36 @@ export async function POST(request: NextRequest) { .toBuffer() as any; } } else { - // 转换为jpeg并优化 imageBuffer = await sharp(imageBuffer) .jpeg({ quality: 85, progressive: true }) .toBuffer() as any; } - await fs.writeFile(filepath, imageBuffer); + // 上传到 my_oss + const formData2 = new FormData(); + const uint8Array = new Uint8Array(imageBuffer); + const blob = new Blob([uint8Array], { type: 'image/jpeg' }); + formData2.append('file', blob, 'image.jpg'); - // 返回相对路径 - const url = `/uploads/${filename}`; - return NextResponse.json({ url }); + const uploadRes = await fetch(`${OSS_URL}/files`, { + method: 'POST', + headers: { + 'x-api-key': API_KEY, + }, + body: formData2, + }); + + if (!uploadRes.ok) { + const err = await uploadRes.text(); + console.error('OSS upload failed:', err); + return NextResponse.json({ error: '上传失败' }, { status: 500 }); + } + + const fileData = await uploadRes.json(); + + // 返回 my_oss 的访问URL + const url = `${OSS_URL}/files/${fileData.fileKey}/preview`; + return NextResponse.json({ url, fileKey: fileData.fileKey }); } catch (error) { console.error('Upload error:', error); return NextResponse.json({ error: '上传失败' }, { status: 500 }); @@ -78,16 +90,24 @@ export async function POST(request: NextRequest) { export async function DELETE(request: NextRequest) { try { const { searchParams } = new URL(request.url); - const filename = searchParams.get('filename'); + const fileKey = searchParams.get('fileKey'); - if (!filename) { - return NextResponse.json({ error: '缺少文件名' }, { status: 400 }); + if (!fileKey) { + return NextResponse.json({ error: '缺少文件标识' }, { status: 400 }); } - const filepath = path.join(UPLOAD_DIR, path.basename(filename)); - await fs.unlink(filepath); + const res = await fetch(`${OSS_URL}/files/${fileKey}`, { + method: 'DELETE', + headers: { + 'x-api-key': API_KEY, + }, + }); - return NextResponse.json({ success: true }); + if (res.ok) { + return NextResponse.json({ success: true }); + } + + return NextResponse.json({ error: '删除失败' }, { status: 500 }); } catch (error) { console.error('Delete error:', error); return NextResponse.json({ error: '删除失败' }, { status: 500 });