diff options
Diffstat (limited to 'lib/file-stroage.ts')
| -rw-r--r-- | lib/file-stroage.ts | 283 |
1 files changed, 283 insertions, 0 deletions
diff --git a/lib/file-stroage.ts b/lib/file-stroage.ts new file mode 100644 index 00000000..ae84f506 --- /dev/null +++ b/lib/file-stroage.ts @@ -0,0 +1,283 @@ +// lib/file-storage.ts - File과 ArrayBuffer를 위한 분리된 함수들 + +import { promises as fs } from "fs"; +import path from "path"; +import crypto from "crypto"; + +interface FileStorageConfig { + baseDir: string; + publicUrl: string; + isProduction: boolean; +} + +// 파일명 해시 생성 유틸리티 +export function generateHashedFileName(originalName: string): string { + const fileExtension = path.extname(originalName); + const fileNameWithoutExt = path.basename(originalName, fileExtension); + + const timestamp = Date.now(); + const randomHash = crypto.createHash('md5') + .update(`${fileNameWithoutExt}-${timestamp}-${Math.random()}`) + .digest('hex') + .substring(0, 8); + + return `${timestamp}-${randomHash}${fileExtension}`; +} + +// ✅ File 저장용 인터페이스 +interface SaveFileOptions { + file: File; + directory: string; + originalName?: string; +} + +// ✅ Buffer/ArrayBuffer 저장용 인터페이스 +interface SaveBufferOptions { + buffer: Buffer | ArrayBuffer; + fileName: string; + directory: string; + originalName?: string; +} + +interface SaveFileResult { + success: boolean; + filePath?: string; + publicPath?: string; + fileName?: string; + error?: string; +} + +const nasPath = process.env.NAS_PATH || "/evcp_nas" + +// 환경별 설정 +function getStorageConfig(): FileStorageConfig { + const isProduction = process.env.NODE_ENV === "production"; + + if (isProduction) { + return { + baseDir: nasPath, + publicUrl: "/api/files", + isProduction: true, + }; + } else { + return { + baseDir: path.join(process.cwd(), "public"), + publicUrl: "", + isProduction: false, + }; + } +} + +// ✅ 1. File 객체 저장 함수 (기존 방식) +export async function saveFile({ + file, + directory, + originalName +}: SaveFileOptions): Promise<SaveFileResult> { + try { + const config = getStorageConfig(); + const finalFileName = originalName || file.name; + const hashedFileName = generateHashedFileName(finalFileName); + + // 저장 경로 설정 + const saveDir = path.join(config.baseDir, directory); + const filePath = path.join(saveDir, hashedFileName); + + // 웹 접근 경로 + let publicPath: string; + if (config.isProduction) { + publicPath = `${config.publicUrl}/${directory}/${hashedFileName}`; + } else { + publicPath = `/${directory}/${hashedFileName}`; + } + + console.log(`📄 File 객체 저장: ${finalFileName}`); + console.log(`📁 저장 위치: ${filePath}`); + console.log(`🌐 웹 접근 경로: ${publicPath}`); + + // 디렉토리 생성 + await fs.mkdir(saveDir, { recursive: true }); + + // File 객체에서 데이터 추출 + const arrayBuffer = await file.arrayBuffer(); + const dataBuffer = Buffer.from(arrayBuffer); + + // 파일 저장 + await fs.writeFile(filePath, dataBuffer); + + console.log(`✅ File 저장 완료: ${hashedFileName} (${dataBuffer.length} bytes)`); + + return { + success: true, + filePath, + publicPath, + fileName: hashedFileName, + }; + } catch (error) { + console.error("File 저장 실패:", error); + return { + success: false, + error: error instanceof Error ? error.message : "File 저장 중 오류가 발생했습니다.", + }; + } +} + +// ✅ 2. Buffer/ArrayBuffer 저장 함수 (DRM 복호화용) +export async function saveBuffer({ + buffer, + fileName, + directory, + originalName +}: SaveBufferOptions): Promise<SaveFileResult> { + try { + const config = getStorageConfig(); + const finalFileName = originalName || fileName; + const hashedFileName = generateHashedFileName(finalFileName); + + // 저장 경로 설정 + const saveDir = path.join(config.baseDir, directory); + const filePath = path.join(saveDir, hashedFileName); + + // 웹 접근 경로 + let publicPath: string; + if (config.isProduction) { + publicPath = `${config.publicUrl}/${directory}/${hashedFileName}`; + } else { + publicPath = `/${directory}/${hashedFileName}`; + } + + console.log(`🔓 Buffer/ArrayBuffer 저장: ${finalFileName}`); + console.log(`📁 저장 위치: ${filePath}`); + console.log(`🌐 웹 접근 경로: ${publicPath}`); + + // 디렉토리 생성 + await fs.mkdir(saveDir, { recursive: true }); + + // Buffer 준비 + const dataBuffer = buffer instanceof ArrayBuffer ? Buffer.from(buffer) : buffer; + + // 파일 저장 + await fs.writeFile(filePath, dataBuffer); + + console.log(`✅ Buffer 저장 완료: ${hashedFileName} (${dataBuffer.length} bytes)`); + + return { + success: true, + filePath, + publicPath, + fileName: hashedFileName, + }; + } catch (error) { + console.error("Buffer 저장 실패:", error); + return { + success: false, + error: error instanceof Error ? error.message : "Buffer 저장 중 오류가 발생했습니다.", + }; + } +} + +// ✅ 업데이트 함수들 +export async function updateFile( + options: SaveFileOptions, + oldFilePath?: string +): Promise<SaveFileResult> { + try { + const result = await saveFile(options); + + if (result.success && oldFilePath) { + await deleteFile(oldFilePath); + } + + return result; + } catch (error) { + console.error("File 업데이트 실패:", error); + return { + success: false, + error: error instanceof Error ? error.message : "File 업데이트 중 오류가 발생했습니다.", + }; + } +} + +export async function updateBuffer( + options: SaveBufferOptions, + oldFilePath?: string +): Promise<SaveFileResult> { + try { + const result = await saveBuffer(options); + + if (result.success && oldFilePath) { + await deleteFile(oldFilePath); + } + + return result; + } catch (error) { + console.error("Buffer 업데이트 실패:", error); + return { + success: false, + error: error instanceof Error ? error.message : "Buffer 업데이트 중 오류가 발생했습니다.", + }; + } +} + +// 파일 삭제 함수 +export async function deleteFile(publicPath: string): Promise<boolean> { + try { + const config = getStorageConfig(); + + let absolutePath: string; + if (config.isProduction) { + const relativePath = publicPath.replace('/api/files/', ''); + absolutePath = path.join(nasPath, relativePath); + } else { + absolutePath = path.join(process.cwd(), 'public', publicPath); + } + + console.log(`🗑️ 파일 삭제: ${absolutePath}`); + + await fs.access(absolutePath); + await fs.unlink(absolutePath); + return true; + } catch (error) { + console.log("파일 삭제 실패 또는 파일이 없음:", error); + return false; + } +} + +// ✅ 편의 함수들 (하위 호환성) +export const save = { + file: saveFile, + buffer: saveBuffer, +}; + +// ✅ DRM 워크플로우 통합 함수 +export async function saveDRMFile( + originalFile: File, + decryptFunction: (file: File) => Promise<ArrayBuffer>, + directory: string +): Promise<SaveFileResult> { + try { + console.log(`🔐 DRM 파일 처리 시작: ${originalFile.name}`); + + // 1. DRM 복호화 + const decryptedData = await decryptFunction(originalFile); + + // 2. 복호화된 데이터 저장 + const result = await saveBuffer({ + buffer: decryptedData, + fileName: originalFile.name, + directory + }); + + if (result.success) { + console.log(`✅ DRM 파일 처리 완료: ${originalFile.name}`); + } + + return result; + } catch (error) { + console.error(`❌ DRM 파일 처리 실패: ${originalFile.name}`, error); + return { + success: false, + error: error instanceof Error ? error.message : "DRM 파일 처리 중 오류가 발생했습니다.", + }; + } +}
\ No newline at end of file |
