From 030fb2af0b2a822f8b93809b008edeb3ffe3e18c Mon Sep 17 00:00:00 2001 From: chenpei Date: Sun, 26 Oct 2025 17:21:39 +0800 Subject: [PATCH] fix: Prevent concurrent upload conflicts with unique file names - Add generateUniqueFileName function using crypto.randomBytes - Replace Date.now() with timestamp + random hex for all file names - Ensures unique file names even when multiple users upload simultaneously - Fixes: temp PSD files, temp preview files, S3 preview files, and material files - Resolves reported issue of upload errors with concurrent users --- app/api/materials/upload/route.ts | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/app/api/materials/upload/route.ts b/app/api/materials/upload/route.ts index 6e870b1f..b66150a6 100644 --- a/app/api/materials/upload/route.ts +++ b/app/api/materials/upload/route.ts @@ -7,6 +7,14 @@ import { processImage, getProcessedFileName } from '@/lib/image-processor' import { generatePsdPreview, hasPsdExtension } from '@/lib/psd-processor' import { writeFile, mkdir, unlink } from 'fs/promises' import path from 'path' +import { randomBytes } from 'crypto' + +// 生成唯一文件名(避免并发冲突) +function generateUniqueFileName(prefix: string, ext: string): string { + const timestamp = Date.now() + const randomId = randomBytes(8).toString('hex') // 16个字符的随机字符串 + return `${prefix}-${timestamp}-${randomId}${ext}` +} // Configure route to handle large files export const runtime = 'nodejs' @@ -105,14 +113,14 @@ export async function POST(req: NextRequest) { const uploadDir = process.env.UPLOAD_DIR || './uploads' await mkdir(uploadDir, { recursive: true }) - const tempPsdFileName = `temp-psd-${Date.now()}-${zipFile.name}` + const tempPsdFileName = generateUniqueFileName('temp-psd', `.${zipFile.name.split('.').pop()}`) const tempPsdPath = path.join(uploadDir, tempPsdFileName) const psdBuffer = Buffer.from(await zipFile.arrayBuffer()) await writeFile(tempPsdPath, psdBuffer) console.log('[Upload] Temporary PSD saved:', tempPsdPath) // Generate preview image - const tempPreviewPath = path.join(uploadDir, `temp-preview-${Date.now()}.jpg`) + const tempPreviewPath = path.join(uploadDir, generateUniqueFileName('temp-preview', '.jpg')) const result = await generatePsdPreview(tempPsdPath, tempPreviewPath, 800) if (!result.success || !result.previewPath) { @@ -147,7 +155,7 @@ export async function POST(req: NextRequest) { console.log('[Upload] Temporary files cleaned up') // Upload to S3 - previewFileName = `preview-${Date.now()}-generated.webp` + previewFileName = generateUniqueFileName('preview', '-generated.webp') previewUrl = await uploadToS3(processedBuffer, previewFileName, 'image/webp') console.log('[Upload] Auto-generated preview uploaded to S3:', previewUrl) @@ -209,7 +217,8 @@ export async function POST(req: NextRequest) { // Upload processed preview image to S3 console.log('[Upload] Uploading image to S3...') const processedFileName = getProcessedFileName(previewImage.name) - previewFileName = `preview-${Date.now()}-${processedFileName}` + const fileExt = path.extname(processedFileName) + previewFileName = generateUniqueFileName('preview', fileExt) previewUrl = await uploadToS3(processedBuffer, previewFileName, 'image/webp') console.log('[Upload] Image uploaded to S3:', previewUrl) } @@ -219,7 +228,8 @@ export async function POST(req: NextRequest) { const uploadDir = process.env.UPLOAD_DIR || './uploads' await mkdir(uploadDir, { recursive: true }) - const zipFileName = `zip-${Date.now()}-${zipFile.name}` + const zipFileExt = path.extname(zipFile.name) + const zipFileName = generateUniqueFileName('material', zipFileExt) const zipFilePath = path.join(uploadDir, zipFileName) // If it's a PSD file, we already have the buffer from above