diff options
Diffstat (limited to 'app/api/document-download/route.ts')
| -rw-r--r-- | app/api/document-download/route.ts | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/app/api/document-download/route.ts b/app/api/document-download/route.ts new file mode 100644 index 00000000..78e31ce2 --- /dev/null +++ b/app/api/document-download/route.ts @@ -0,0 +1,173 @@ +// app/api/document-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 { documentAttachments } from '@/db/schema/vendorDocu'; +import { eq } from 'drizzle-orm'; + +export async function GET(request: NextRequest) { + try { + // 파일 경로 파라미터 받기 + const path = request.nextUrl.searchParams.get("path"); + const attachmentId = request.nextUrl.searchParams.get("id"); + + console.log(path) + + if (!path && !attachmentId) { + return NextResponse.json( + { error: "File path or attachment ID is required" }, + { status: 400 } + ); + } + + let dbRecord; + + // ID로 조회하는 경우 (더 안전함) + if (attachmentId) { + [dbRecord] = await db + .select({ + fileName: documentAttachments.fileName, + filePath: documentAttachments.filePath, + fileType: documentAttachments.fileType, + fileSize: documentAttachments.fileSize + }) + .from(documentAttachments) + .where(eq(documentAttachments.id, parseInt(attachmentId))); + } + // 경로로 조회하는 경우 + else if (path) { + [dbRecord] = await db + .select({ + fileName: documentAttachments.fileName, + filePath: documentAttachments.filePath, + fileType: documentAttachments.fileType, + fileSize: documentAttachments.fileSize + }) + .from(documentAttachments) + .where(eq(documentAttachments.filePath, path)); + } + + // 파일 정보 설정 + let fileName; + let actualFilePath; + + if (dbRecord) { + // DB에서 찾은 경우 원본 파일명과 경로 사용 + fileName = dbRecord.fileName; + actualFilePath = dbRecord.filePath; + console.log("DB에서 파일 정보 찾음:", { fileName, filePath: actualFilePath }); + } else { + // DB에서 찾지 못한 경우 + if (!path) { + return NextResponse.json( + { error: "File not found in database" }, + { status: 404 } + ); + } + fileName = path.split('/').pop() || 'download'; + actualFilePath = path; + } + + // 파일 경로 구성 + const storedPath = actualFilePath.replace(/^\/+/, ""); // 앞쪽 슬래시 제거 + + // 파일 경로 시도 + const possiblePaths = [ + join(process.cwd(), "public", storedPath), + join(process.cwd(), storedPath), // public 없이도 시도 + ]; + + // 실제 파일 찾기 + let foundPath = null; + for (const testPath of possiblePaths) { + try { + await access(testPath, constants.R_OK); + foundPath = testPath; + console.log("✅ 파일 찾음:", testPath); + break; + } catch (err) { + console.log("❌ 경로에 파일 없음:", testPath); + } + } + + if (!foundPath) { + return NextResponse.json( + { + error: "File not found on server", + details: { + fileName: fileName, + path: actualFilePath, + triedPaths: possiblePaths + } + }, + { status: 404 } + ); + } + + const fileBuffer = await readFile(foundPath); + + // MIME 타입 결정 + const fileExtension = fileName.split('.').pop()?.toLowerCase() || ''; + let contentType = dbRecord?.fileType || 'application/octet-stream'; + + // DB에 타입이 없거나 generic한 경우 확장자로 추론 + if (!contentType || contentType === 'application/octet-stream') { + 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', + 'dwg': 'application/acad', + 'dxf': 'application/dxf', + 'zip': 'application/zip', + 'rar': 'application/vnd.rar', + }; + contentType = mimeTypes[fileExtension] || 'application/octet-stream'; + } + + // 다운로드용 헤더 설정 + const headers = new Headers(); + headers.set('Content-Type', contentType); + headers.set('Content-Disposition', `attachment; filename="${encodeURIComponent(fileName)}"`); + headers.set('Content-Length', fileBuffer.length.toString()); + + // 파일 크기 검증 (DB 정보와 비교) + if (dbRecord?.fileSize && Math.abs(fileBuffer.length - dbRecord.fileSize) > 1024) { + console.warn("⚠️ 파일 크기 불일치:", { + dbSize: dbRecord.fileSize, + actualSize: fileBuffer.length + }); + } + + console.log("✅ 파일 다운로드 성공:", { + fileName, + size: fileBuffer.length, + contentType + }); + + return new NextResponse(fileBuffer, { + status: 200, + headers, + }); + + } catch (error) { + console.error('❌ 문서 파일 다운로드 오류:', error); + return NextResponse.json( + { + error: 'Failed to download file', + details: String(error) + }, + { status: 500 } + ); + } +}
\ No newline at end of file |
