/** * 파일명에서 확장자를 추출한다. (브라우저·서버 공용, node:path 미사용) * @param {string} [filename] - 파일명 * @returns {string} 소문자 확장자(점 포함, 예: `.mp4`) */ const getFileExtension = (filename = '') => { const normalized = String(filename).trim() const lastDot = normalized.lastIndexOf('.') if (lastDot <= 0 || lastDot === normalized.length - 1) { return '' } return normalized.slice(lastDot).toLowerCase() } /** @type {Record} 업로드 종류 */ export const UPLOAD_KIND = { image: 'image', video: 'video', audio: 'audio', document: 'document' } const VIDEO_EXTENSIONS = new Set(['.mp4', '.webm', '.mov']) const AUDIO_EXTENSIONS = new Set(['.mp3', '.wav', '.ogg', '.m4a']) const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif']) /** @type {Record} 업로드 종류 한글 라벨 */ const UPLOAD_KIND_LABELS = { [UPLOAD_KIND.image]: '이미지', [UPLOAD_KIND.video]: '비디오', [UPLOAD_KIND.audio]: '오디오', [UPLOAD_KIND.document]: '파일' } /** * 기본 업로드 크기 한도(바이트)를 만든다. * @param {Object} [overrides] - 종류별 한도 덮어쓰기 * @returns {{ image: number, video: number, audio: number, document: number }} 종류별 한도 */ export const buildDefaultUploadSizeLimits = (overrides = {}) => ({ image: 10485760, video: 209715200, audio: 52428800, document: 52428800, ...overrides }) /** * MIME·파일명으로 업로드 종류를 판별한다. * @param {string} [mimeType] - MIME 타입 * @param {string} [filename] - 파일명 * @returns {'image'|'video'|'audio'|'document'} 업로드 종류 */ export const getUploadKind = (mimeType = '', filename = '') => { const extension = getFileExtension(filename) if (mimeType.startsWith('video/') || VIDEO_EXTENSIONS.has(extension)) { return UPLOAD_KIND.video } if (mimeType.startsWith('audio/') || AUDIO_EXTENSIONS.has(extension)) { return UPLOAD_KIND.audio } if (mimeType.startsWith('image/') || IMAGE_EXTENSIONS.has(extension)) { return UPLOAD_KIND.image } return UPLOAD_KIND.document } /** * 업로드 종류별 최대 바이트를 반환한다. * @param {'image'|'video'|'audio'|'document'} kind - 업로드 종류 * @param {{ image: number, video: number, audio: number, document: number }} limits - 종류별 한도 * @returns {number} 최대 바이트 */ export const getMaxUploadBytesForKind = (kind, limits) => limits[kind] ?? limits.image /** * 바이트를 사람이 읽기 쉬운 용량 문자열로 변환한다. * @param {number} bytes - 바이트 * @returns {string} 용량 문자열 */ export const formatUploadSizeLimit = (bytes) => { if (!Number.isFinite(bytes) || bytes <= 0) { return '0B' } if (bytes >= 1073741824) { const gigabytes = bytes / 1073741824 return `${Number.isInteger(gigabytes) ? gigabytes : gigabytes.toFixed(1)}GB` } if (bytes >= 1048576) { return `${Math.round(bytes / 1048576)}MB` } if (bytes >= 1024) { return `${Math.round(bytes / 1024)}KB` } return `${bytes}B` } /** * 업로드 종류 한글 라벨을 반환한다. * @param {'image'|'video'|'audio'|'document'} kind - 업로드 종류 * @returns {string} 한글 라벨 */ export const getUploadKindLabel = (kind) => UPLOAD_KIND_LABELS[kind] || UPLOAD_KIND_LABELS[UPLOAD_KIND.image]