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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
// app/api/rfq-download/route.ts
import { NextRequest, NextResponse } from 'next/server';
import { readFile, access, constants } from 'fs/promises';
import { join } from 'path';
import db from '@/db/db';
import { rfqAttachments, vendorResponseAttachments } from '@/db/schema/rfq';
import { eq } from 'drizzle-orm';
export async function GET(request: NextRequest) {
try {
// 파일 경로 파라미터 받기
const path = request.nextUrl.searchParams.get("path");
if (!path) {
return NextResponse.json(
{ error: "File path is required" },
{ status: 400 }
);
}
// DB에서 파일 정보 조회 (정확히 일치하는 filePath로 검색)
const [dbRecord] = await db
.select({
fileName: vendorResponseAttachments.fileName,
filePath: vendorResponseAttachments.filePath
})
.from(vendorResponseAttachments)
.where(eq(vendorResponseAttachments.filePath, path));
// 파일 정보 설정
let fileName;
if (dbRecord) {
// DB에서 찾은 경우 원본 파일명 사용
fileName = dbRecord.fileName;
console.log("DB에서 원본 파일명 찾음:", fileName);
} else {
// DB에서 찾지 못한 경우 경로에서 파일명 추출
fileName = path.split('/').pop() || 'download';
}
// 파일 경로 구성
const storedPath = path.replace(/^\/+/, ""); // 앞쪽 슬래시 제거
// 파일 경로 시도
const possiblePaths = [
join(process.cwd(), "public", storedPath)
];
// 실제 파일 찾기
let actualPath = null;
for (const testPath of possiblePaths) {
try {
await access(testPath, constants.R_OK);
actualPath = testPath;
break;
} catch (err) {
console.log("❌ 경로에 파일 없음:", testPath);
}
}
if (!actualPath) {
return NextResponse.json(
{
error: "File not found on server",
details: {
path: path,
triedPaths: possiblePaths
}
},
{ status: 404 }
);
}
const fileBuffer = await readFile(actualPath);
// MIME 타입 결정
const fileExtension = fileName.split('.').pop()?.toLowerCase() || '';
let contentType = 'application/octet-stream'; // 기본 바이너리
// 확장자에 따른 MIME 타입 매핑
const mimeTypes: Record<string, string> = {
'pdf': 'application/pdf',
'doc': 'application/msword',
'docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xls': 'application/vnd.ms-excel',
'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'ppt': 'application/vnd.ms-powerpoint',
'pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'txt': 'text/plain',
'csv': 'text/csv',
'png': 'image/png',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
};
contentType = mimeTypes[fileExtension] || contentType;
// 다운로드용 헤더 설정
const headers = new Headers();
headers.set('Content-Type', contentType);
headers.set('Content-Disposition', `attachment; filename="${encodeURIComponent(fileName)}"`);
headers.set('Content-Length', fileBuffer.length.toString());
return new NextResponse(fileBuffer, {
status: 200,
headers,
});
} catch (error) {
console.error('❌ RFQ 파일 다운로드 오류:', error);
return NextResponse.json(
{
error: 'Failed to download file',
details: String(error)
},
{ status: 500 }
);
}
}
|