diff options
Diffstat (limited to 'app/api/vendors/attachments/download')
| -rw-r--r-- | app/api/vendors/attachments/download/route.ts | 93 |
1 files changed, 93 insertions, 0 deletions
diff --git a/app/api/vendors/attachments/download/route.ts b/app/api/vendors/attachments/download/route.ts new file mode 100644 index 00000000..0151a699 --- /dev/null +++ b/app/api/vendors/attachments/download/route.ts @@ -0,0 +1,93 @@ +// /app/api/vendors/attachments/download/route.js (Next.js App Router 기준) +import { NextRequest, NextResponse } from 'next/server'; +import fs from 'fs'; +import path from 'path'; +import { eq } from 'drizzle-orm'; // 쿼리 빌더 +import { vendorAttachments } from '@/db/schema'; +import db from '@/db/db'; + +export async function GET(request: NextRequest) { + try { + const { searchParams } = new URL(request.url); + const fileId = searchParams.get('id'); + const vendorId = searchParams.get('vendorId'); + + if (!fileId || !vendorId) { + return NextResponse.json( + { error: "필수 파라미터가 누락되었습니다." }, + { status: 400 } + ); + } + + // 첨부파일 정보 조회 + const attachment = await db.query.vendorAttachments.findFirst({ + where: eq(vendorAttachments.id, parseInt(fileId, 10)) + }); + + if (!attachment) { + return NextResponse.json( + { error: "파일을 찾을 수 없습니다." }, + { status: 404 } + ); + } + + // 파일 경로 구성 + const basePath = process.env.UPLOAD_DIR || path.join(process.cwd(), 'public'); + const filePath = path.join(basePath, attachment.filePath); + + // 파일 존재 확인 + try { + await fs.promises.access(filePath, fs.constants.F_OK); + } catch (e) { + return NextResponse.json( + { error: "파일이 서버에 존재하지 않습니다." }, + { status: 404 } + ); + } + + // 파일 데이터 읽기 + const fileBuffer = await fs.promises.readFile(filePath); + + + + // 파일 MIME 타입 추정 + let contentType = 'application/octet-stream'; + if (attachment.fileName) { + const ext = path.extname(attachment.fileName).toLowerCase(); + switch (ext) { + case '.pdf': contentType = 'application/pdf'; break; + case '.jpg': + case '.jpeg': contentType = 'image/jpeg'; break; + case '.png': contentType = 'image/png'; break; + case '.doc': contentType = 'application/msword'; break; + case '.docx': contentType = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; break; + // 필요에 따라 더 많은 타입 추가 + } + } + + // 응답 헤더 설정 + const headers = new Headers(); + + // 파일명에 non-ASCII 문자가 포함될 수 있으므로 인코딩 처리 + const encodedFileName = encodeURIComponent(attachment.fileName) + .replace(/['()]/g, escape) // 추가 이스케이프 필요한 문자들 + .replace(/\*/g, '%2A'); + + // RFC 5987에 따른 인코딩 방식 적용 + headers.set('Content-Disposition', `attachment; filename*=UTF-8''${encodedFileName}`); + headers.set('Content-Type', contentType); + headers.set('Content-Length', fileBuffer.length.toString()); + // 파일 데이터와 함께 응답 + return new Response(fileBuffer, { + status: 200, + headers + }); + + } catch (error) { + console.error('파일 다운로드 오류:', error); + return NextResponse.json( + { error: "파일 다운로드 중 오류가 발생했습니다." }, + { status: 500 } + ); + } +}
\ No newline at end of file |
