1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
// app/api/upload-attachment/route.ts
import { NextRequest, NextResponse } from 'next/server'
import { writeFile, mkdir } from 'fs/promises'
import { join } from 'path'
import { randomUUID } from 'crypto'
import db from '@/db/db'
import { vendorEvaluationAttachments } from '@/db/schema'
export async function POST(request: NextRequest) {
try {
const formData = await request.formData()
const files = formData.getAll('files') as File[]
const submissionId = formData.get('submissionId') as string
const responseId = formData.get('responseId') as string
const uploadedBy = formData.get('uploadedBy') as string
if (!files || files.length === 0) {
return NextResponse.json({ error: '파일이 선택되지 않았습니다.' }, { status: 400 })
}
if (!submissionId || !responseId || !uploadedBy) {
return NextResponse.json({ error: '필수 매개변수가 누락되었습니다.' }, { status: 400 })
}
const uploadedFiles = []
// uploads 디렉토리 생성 (없는 경우)
const uploadsDir = join(process.cwd(), 'public', 'uploads', 'attachments')
try {
await mkdir(uploadsDir, { recursive: true })
} catch (error) {
// 디렉토리가 이미 존재하는 경우 무시
}
for (const file of files) {
// 파일 검증
if (file.size > 10 * 1024 * 1024) { // 10MB 제한
return NextResponse.json({ error: `파일 ${file.name}이 너무 큽니다. (최대 10MB)` }, { status: 400 })
}
// 파일 확장자 검증 (선택적)
const allowedTypes = [
'image/jpeg', 'image/png', 'image/gif', 'image/webp',
'application/pdf',
'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'text/plain', 'text/csv'
]
if (!allowedTypes.includes(file.type)) {
return NextResponse.json({ error: `지원되지 않는 파일 형식입니다: ${file.name}` }, { status: 400 })
}
// 고유한 파일명 생성
const fileExtension = file.name.split('.').pop() || ''
const storedFileName = `${randomUUID()}.${fileExtension}`
const filePath = join('uploads', 'attachments', storedFileName)
const fullPath = join(process.cwd(), 'public', filePath)
try {
// 파일 저장
const bytes = await file.arrayBuffer()
const buffer = Buffer.from(bytes)
await writeFile(fullPath, buffer)
// 데이터베이스에 파일 정보 저장
const [savedAttachment] = await db
.insert(vendorEvaluationAttachments)
.values({
submissionId: parseInt(submissionId),
generalEvaluationResponseId: parseInt(responseId),
originalFileName: file.name,
storedFileName: storedFileName,
filePath: filePath,
fileSize: file.size,
mimeType: file.type,
uploadedBy: uploadedBy,
isActive: true,
})
.returning()
uploadedFiles.push({
id: savedAttachment.id,
fileId: savedAttachment.fileId,
originalFileName: savedAttachment.originalFileName,
fileSize: savedAttachment.fileSize,
mimeType: savedAttachment.mimeType,
})
} catch (fileError) {
console.error(`파일 저장 실패: ${file.name}`, fileError)
return NextResponse.json({ error: `파일 저장에 실패했습니다: ${file.name}` }, { status: 500 })
}
}
return NextResponse.json({
success: true,
files: uploadedFiles,
message: `${uploadedFiles.length}개 파일이 성공적으로 업로드되었습니다.`
})
} catch (error) {
console.error('파일 업로드 오류:', error)
return NextResponse.json({ error: '파일 업로드에 실패했습니다.' }, { status: 500 })
}
}
|