53 lines
2.4 KiB
TypeScript
53 lines
2.4 KiB
TypeScript
/**
|
||
* 预览类型判定(纯函数,无副作用,便于单测与复用)。
|
||
*
|
||
* <p>为什么集中在一个文件:前端多处 `FileList` 预览都按「后缀 → 渲染方式」分流,
|
||
* 统一这里避免每个页面各写一套正则 / 后缀表。后端同源预览端点(`/web/workOrderDraft/preview/{ossId}`)
|
||
* 按后缀纠正 Content-Type,这里与之后端口径保持一致的归类。</p>
|
||
*/
|
||
export type PreviewKind = 'image' | 'pdf' | 'video' | 'audio' | 'text' | 'unsupported'
|
||
|
||
const IMAGE_SUFFIXES = ['png', 'jpg', 'jpeg', 'gif', 'bmp', 'webp']
|
||
const PDF_SUFFIXES = ['pdf']
|
||
const VIDEO_SUFFIXES = ['mp4', 'webm', 'mov', 'avi', 'm4v']
|
||
const AUDIO_SUFFIXES = ['mp3', 'wav', 'ogg', 'm4a']
|
||
// 文本类:含 html/htm/svg —— 统一按纯文本展示源码,避免内联渲染触发存储型 XSS
|
||
const TEXT_SUFFIXES = ['txt', 'sql', 'log', 'csv', 'json', 'xml', 'md', 'yml', 'yaml', 'html', 'htm', 'svg']
|
||
|
||
/** 取小写字尾(不含点);无点返回空串 */
|
||
export function getSuffix(name: string): string {
|
||
const idx = name.lastIndexOf('.')
|
||
return idx >= 0 ? name.slice(idx + 1).toLowerCase() : ''
|
||
}
|
||
|
||
/** 按文件名判定预览类型;office / 未知后缀一律 unsupported(前端不内联,给下载) */
|
||
export function resolvePreviewKind(name: string): PreviewKind {
|
||
const ext = getSuffix(name)
|
||
if (IMAGE_SUFFIXES.includes(ext)) return 'image'
|
||
if (PDF_SUFFIXES.includes(ext)) return 'pdf'
|
||
if (VIDEO_SUFFIXES.includes(ext)) return 'video'
|
||
if (AUDIO_SUFFIXES.includes(ext)) return 'audio'
|
||
if (TEXT_SUFFIXES.includes(ext)) return 'text'
|
||
return 'unsupported'
|
||
}
|
||
|
||
/** 是否可被前端内联预览(true = image/pdf/video/audio/text) */
|
||
export function isPreviewable(name: string): boolean {
|
||
return resolvePreviewKind(name) !== 'unsupported'
|
||
}
|
||
|
||
/**
|
||
* 文本内联预览上限(1MB)。
|
||
* 整文件塞进 `<pre>` 会卡死浏览器(如 5MB+ 的 .sql/.log),故只读取前 1MB 渲染,超出截断并提示下载。
|
||
*/
|
||
export const PREVIEW_TEXT_MAX_BYTES = 1024 * 1024
|
||
|
||
/** 人类可读体积(B/KB/MB/GB) */
|
||
export function formatBytes(bytes: number): string {
|
||
if (!bytes || bytes < 0) return '0 B'
|
||
const units = ['B', 'KB', 'MB', 'GB']
|
||
const i = Math.min(units.length - 1, Math.floor(Math.log(bytes) / Math.log(1024)))
|
||
const val = bytes / Math.pow(1024, i)
|
||
return `${i === 0 ? val : val.toFixed(1)} ${units[i]}`
|
||
}
|