summaryrefslogtreecommitdiff
path: root/lib/file-stroage.ts
diff options
context:
space:
mode:
authordujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
committerdujinkim <dujin.kim@dtsolution.co.kr>2025-07-07 01:44:45 +0000
commit90f79a7a691943a496f67f01c1e493256070e4de (patch)
tree37275fde3ae08c2bca384fbbc8eb378de7e39230 /lib/file-stroage.ts
parentfbb3b7f05737f9571b04b0a8f4f15c0928de8545 (diff)
(대표님) 변경사항 20250707 10시 43분 - unstaged 변경사항 추가
Diffstat (limited to 'lib/file-stroage.ts')
-rw-r--r--lib/file-stroage.ts283
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