- claudedocs 폴더 구조 재정리: archive/sessions, guides/migration·mobile·universal-list, refactoring 분류 - 오래된 세션 컨텍스트/체크리스트 문서 정리 (아카이브 이동 또는 삭제) - AuthContext → authStore(Zustand) 전환 시작, RootProvider 간소화 - GenericCRUDDialog 공통 다이얼로그 컴포넌트 추가 - PermissionDialog 삭제 → GenericCRUDDialog로 대체 - RankDialog/TitleDialog GenericCRUDDialog 기반으로 리팩토링 - toast-utils.ts 삭제 (미사용) - fileDownload.ts 개선, excel-download.ts 정리 - menuStore/themeStore Zustand 셀렉터 최적화 - useColumnSettings/useTableColumnStore 기능 보강 - 세금계산서/견적/작업자화면/결재 등 소규모 개선 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
/**
|
|
* 파일 다운로드 유틸리티
|
|
*
|
|
* 백엔드 API: GET /api/v1/files/{id}/download
|
|
* 프록시: GET /api/proxy/files/{id}/download
|
|
*/
|
|
|
|
import { downloadBlob } from './export';
|
|
|
|
/**
|
|
* Content-Disposition 헤더에서 파일명 추출
|
|
*/
|
|
function extractFilenameFromHeader(response: Response): string | null {
|
|
const contentDisposition = response.headers.get('Content-Disposition');
|
|
if (!contentDisposition) return null;
|
|
|
|
const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
|
if (!match?.[1]) return null;
|
|
|
|
const raw = match[1].replace(/['"]/g, '');
|
|
try {
|
|
return decodeURIComponent(raw);
|
|
} catch {
|
|
return raw;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 파일 ID로 다운로드
|
|
* @param fileId 파일 ID
|
|
* @param fileName 저장할 파일명 (선택, 없으면 서버에서 제공하는 이름 사용)
|
|
*/
|
|
export async function downloadFileById(fileId: number, fileName?: string): Promise<void> {
|
|
try {
|
|
const response = await fetch(`/api/proxy/files/${fileId}/download`);
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`다운로드 실패: ${response.status}`);
|
|
}
|
|
|
|
const blob = await response.blob();
|
|
const downloadFileName = fileName
|
|
?? extractFilenameFromHeader(response)
|
|
?? `file_${fileId}`;
|
|
|
|
downloadBlob(blob, downloadFileName);
|
|
} catch (error) {
|
|
console.error('[fileDownload] 다운로드 오류:', error);
|
|
throw error;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 파일 경로로 새 탭에서 열기 (미리보기용)
|
|
* @param filePath 파일 경로
|
|
*/
|
|
export function openFileInNewTab(filePath: string): void {
|
|
// 백엔드 파일 서빙 URL 구성
|
|
const baseUrl = process.env.NEXT_PUBLIC_API_URL || '';
|
|
const fileUrl = `${baseUrl}/storage/${filePath}`;
|
|
window.open(fileUrl, '_blank');
|
|
} |