/** * 검증게시판 첨부 — 이미지 + zip·hwp·hwpx */ import { pickImageFiles, prepareImagesForUpload, } from './clipboardImageFiles'; export const VERIFICATION_ATTACHMENT_ACCEPT = 'image/jpeg,image/png,image/gif,image/webp,image/bmp,.zip,.hwp,.hwpx,application/zip,application/x-hwp,application/vnd.hancom.hwpx'; const DOCUMENT_EXT = new Set(['.zip', '.hwp', '.hwpx']); const DOCUMENT_MIME = new Set([ 'application/zip', 'application/x-zip-compressed', 'application/x-hwp', 'application/haansoft-hwp', 'application/vnd.hancom.hwp', 'application/hwp+zip', 'application/vnd.hancom.hwpx', 'application/x-hwpx', ]); function extOf(name: string): string { const i = name.lastIndexOf('.'); return i >= 0 ? name.slice(i).toLowerCase() : ''; } function mimeForDocumentExt(ext: string): string | null { switch (ext) { case '.zip': return 'application/zip'; case '.hwp': return 'application/x-hwp'; case '.hwpx': return 'application/vnd.hancom.hwpx'; default: return null; } } export function isImageAttachment(contentType?: string | null, fileName?: string | null): boolean { const mime = (contentType ?? '').split(';')[0]?.trim().toLowerCase(); if (mime.startsWith('image/')) return true; const ext = fileName ? extOf(fileName) : ''; return ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp'].includes(ext); } export function isDocumentAttachmentFile(file: File): boolean { const ext = extOf(file.name); if (DOCUMENT_EXT.has(ext)) return true; const mime = file.type.split(';')[0]?.trim().toLowerCase(); return DOCUMENT_MIME.has(mime); } function normalizeDocumentFile(file: File): File | null { if (file.size === 0) return null; const ext = extOf(file.name); if (!DOCUMENT_EXT.has(ext)) { const mime = file.type.split(';')[0]?.trim().toLowerCase(); if (!DOCUMENT_MIME.has(mime)) return null; } const mime = mimeForDocumentExt(ext) ?? file.type.split(';')[0]?.trim().toLowerCase() ?? 'application/octet-stream'; if (file.type === mime) return file; return new File([file], file.name, { type: mime, lastModified: file.lastModified }); } /** FileList / File[] → 허용 첨부만 (이미지·zip·hwp·hwpx) */ export function pickAttachmentFiles(list: FileList | File[]): File[] { const out: File[] = []; for (const file of Array.from(list)) { if (isDocumentAttachmentFile(file)) { const doc = normalizeDocumentFile(file); if (doc) out.push(doc); continue; } const img = pickImageFiles([file])[0]; if (img) out.push(img); } return out; } const MAX_BYTES = 5 * 1024 * 1024; /** 이미지는 압축·문서는 그대로 업로드 */ export async function prepareAttachmentsForUpload(files: File[]): Promise { const picked = pickAttachmentFiles(files); const out: File[] = []; for (const f of picked) { if (isDocumentAttachmentFile(f)) { if (f.size > MAX_BYTES) throw new Error('FILE_TOO_LARGE'); out.push(f); } else { out.push(...await prepareImagesForUpload([f])); } } return out; } export const ATTACHMENT_TYPE_HINT = 'jpeg, png, gif, webp, bmp, zip, hwp, hwpx (파일당 5MB)';