summaryrefslogtreecommitdiff
path: root/app/api/document-download
diff options
context:
space:
mode:
Diffstat (limited to 'app/api/document-download')
-rw-r--r--app/api/document-download/route.ts173
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