/** Имя файла из Content-Disposition: `filename*=UTF-8''…` важнее `filename=`. */ export function filenameFromContentDisposition(header: string | null): string | null { if (!header) { return null; } const star = /filename\*\s*=\s*(?:UTF-8|utf-8)''([^;]+)/.exec(header); if (star) { try { return decodeURIComponent(star[1].trim()); } catch { return star[1].trim(); } } const plain = /filename\s*=\s*"?([^";]+)"?/.exec(header); return plain ? plain[1].trim() : null; } const safeName = (name: string) => name.replace(/[\\/:*?"<>|\s]+/g, '_'); /** Запасное имя экспорта свода, если Content-Disposition недоступен. */ export function exportFilename(groupName: string, year: number, quarter: string): string { return `Сводная_ОСВ_${safeName(groupName)}_${year}_${quarter}.xlsx`; } /** Запасное имя xlsx-выгрузки версии: `<префикс>_<ГК>_<год>_<квартал>.xlsx`, без карточки версии — `<префикс>.xlsx`. */ export function xlsxFilename(prefix: string, version: { groupName: string; year: number; quarter: string } | null): string { if (!version) { return `${prefix}.xlsx`; } return `${prefix}_${safeName(version.groupName)}_${version.year}_${version.quarter}.xlsx`; } /** Сохранить blob файлом через временную ссылку. */ export function saveBlob(blob: Blob, filename: string): void { const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; document.body.appendChild(link); link.click(); link.remove(); // не сразу: Safari/Firefox иначе могут оборвать скачивание setTimeout(() => URL.revokeObjectURL(url), 1000); }