2026-07-13 17:42:27 +08:00

87 lines
2.8 KiB
TypeScript
Raw 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 { getAuthenticatedUploadUserId } from '@/lib/upload-auth'
import { saveUploadSession } from '@/lib/upload-sessions'
import { randomBytes } from 'crypto'
import { mkdir, writeFile } from 'fs/promises'
import path from 'path'
export async function POST(req: NextRequest) {
try {
const userId = await getAuthenticatedUploadUserId(req)
if (!userId) {
return NextResponse.json({ error: '请先登录' }, { status: 401 })
}
let body
try {
body = await req.json()
} catch (jsonError) {
console.error('[UploadInit] JSON解析错误:', jsonError)
return NextResponse.json(
{ error: '请求体格式错误,需要JSON格式' },
{ status: 400 }
)
}
const normalizedFileName = typeof body?.fileName === 'string' ? body.fileName.trim() : ''
const normalizedFileSize = Number(body?.fileSize)
const normalizedTotalChunks = Number(body?.totalChunks)
if (!normalizedFileName) {
return NextResponse.json({ error: '文件名不能为空' }, { status: 400 })
}
if (!Number.isFinite(normalizedFileSize) || normalizedFileSize <= 0) {
return NextResponse.json({ error: '上传文件不能为空' }, { status: 400 })
}
if (!Number.isInteger(normalizedTotalChunks) || normalizedTotalChunks <= 0) {
return NextResponse.json({ error: '上传分片参数无效' }, { status: 400 })
}
// 生成上传ID
const uploadId = `upload-${Date.now()}-${randomBytes(16).toString('hex')}`
// 创建临时目录
const uploadDir = process.env.UPLOAD_DIR || '/app/uploads'
const tempDir = path.join(uploadDir, 'temp', uploadId)
await mkdir(tempDir, { recursive: true })
// 写入元数据,方便在Redis丢失时回溯
const metaPath = path.join(tempDir, 'meta.json')
const metaPayload = {
fileName: normalizedFileName,
fileSize: normalizedFileSize,
totalChunks: normalizedTotalChunks,
userId,
createdAt: Date.now(),
}
await writeFile(metaPath, JSON.stringify(metaPayload), 'utf-8')
// 保存会话(使用统一的接口,自动选择Redis或内存Map)
const now = Date.now()
await saveUploadSession(uploadId, {
fileName: normalizedFileName,
fileSize: normalizedFileSize,
totalChunks: normalizedTotalChunks,
chunks: new Map(),
createdAt: now,
lastActivityAt: now,
uploadedBytes: 0,
currentSpeed: 0,
_speedSampleAt: now,
_speedSampleBytes: 0,
userId,
})
console.log(`[UploadInit] Created upload session: ${uploadId}, chunks: ${normalizedTotalChunks}`)
return NextResponse.json({ uploadId })
} catch (error) {
console.error('[UploadInit] Error:', error)
return NextResponse.json(
{ error: '初始化上传失败' },
{ status: 500 }
)
}
}