前端文件预览别再开盲盒了:Flyfish File Viewer 2.1 支持 206+ 格式,纯前端全能预览还模块化
如果你在企业系统里做过“附件预览”,大概率见过这种场景:
产品说:“就是点一下看看文件,很简单吧?”
然后用户上传了:
- 一个 PDF 合同
- 一个 Word 公文
- 一个 Excel 台账
- 一个 DWG 图纸
- 一个 ZIP 压缩包
- 一个 MSG 邮件
- 一个 XMind 脑图
- 一个 Typst 源文件
- 一个 PSD 设计稿
- 一个 GDSII 版图文件
- 一个 Mermaid 架构图
- 一个 Git patch
前端同学看完列表,咖啡都凉了。
这就是 File Viewer 想解决的问题:把复杂附件预览做成一个纯前端、可按需装配、跨框架、可私有化部署的组件体系。
官网:file-viewer.app
文档:doc.file-viewer.app
在线 Demo:demo.file-viewer.app
文档比对 Demo:demo.file-viewer.app/compare.htm…
GitHub:github.com/flyfish-dev…
协议:Apache-2.0,支持商用、二开和企业内部集成
这篇文章适合谁看
如果你的项目里有下面任意一个需求,这篇文章应该能帮你快速判断 File Viewer 是否适合:
| 场景 | 常见问题 |
|---|---|
| OA / BPM / 合同系统 | PDF、Word、OFD 要在线打开,还要水印、下载、打印、权限校验 |
| 知识库 / 文档中心 | Markdown、Office、PDF、代码文件、图片都要能预览 |
| 低代码 / 中后台平台 | 希望一个组件给多个业务线复用,参数、事件和样式要统一 |
| 客服 / 工单系统 | EML、MSG、MBOX 邮件和附件要在线查看 |
| 工程图纸系统 | DWG、DXF、DWF、DWFx、XPS、3D 模型、GDSII 等文件要浏览 |
| 研发协作平台 | patch、bundle、日志、配置文件和代码片段要能快速阅读 |
| 内网系统 | Worker、WASM、字体、PDF.js、CAD 资源都要能自托管 |
简单说:它不是只给“文档系统”用的,更适合各种业务系统里的附件中心、预览中心、知识库、流程系统和工程资料平台。
一句话介绍 File Viewer
File Viewer 2.1 是一个浏览器原生多格式文件预览超级组件。
它目前公开支持矩阵覆盖:
| 维度 | 当前能力 |
|---|---|
| 扩展名 | 206+ |
| 预览链路 | 24 条 |
| npm 发布目标 | 42 个 |
| 许可证 | Apache-2.0 |
| 核心形态 | 纯前端、TypeScript、模块化 renderer |
| 组件生态 | Pure Web、Vue3、Vue2.7、Vue2.6、React、React Legacy、jQuery、Svelte |
| 部署方式 | npm、静态资源、Docker、Cloudflare Pages、自托管 Worker/WASM |
它的重点不是“塞进一个很大的万能包”,而是把底层 core、具体格式 renderer、业务 preset、Vite 自动装配插件和各框架组件拆清楚。
架构变化:从一个大组件,拆成可组合的预览体系
过去很多文件预览组件都会越写越重。看 PDF 要 PDF.js,看 Word 要 DOCX 解析器,看 CAD 要 WASM,看压缩包要 libarchive,看 Typst 要编译器,看 3D 要 Three.js。
如果这些能力全部跟着主包一起进入项目,用户只想看一张图片,也要为 CAD、Typst、PDF、Office 依赖买单。
File Viewer 2.1 的架构拆成四层:
| 层级 | 职责 |
|---|---|
@file-viewer/core | 文件源加载、格式识别、renderer registry、生命周期、搜索、定位、缩放、打印、导出、统一类型和事件 |
@file-viewer/renderer-* | 单条或一组格式链路,例如 PDF、Word、CAD、Typst、Archive、Email、Drawing、EDA |
@file-viewer/preset-* | 按业务形态组合 renderer,例如 lite、office、engineering、all |
| 标准组件包 | Vue、React、Svelte、jQuery、Pure Web 等生态原生组件体验 |
这样的好处很明显:
- core 保持轻量,不绑定 Vue / React / Svelte
- PDF、Office、CAD、Typst、压缩包、邮件、3D、EDA 等重链路按需加载
- 各框架组件共享同一套 options、事件、controller API
- 新格式可以通过独立 renderer 演进
- 企业内网部署时,Worker、WASM、字体和 vendor 资源都有明确出口
从轻量到全功能,应该怎么选包
File Viewer 2.1 不建议所有业务默认全量安装。更好的方式是按文件范围选择。
| 业务范围 | 推荐组合 | 适合场景 |
|---|---|---|
| 轻量附件 | @file-viewer/preset-lite | 文本、Markdown、代码、图片、音频、视频 |
| 办公文档 | @file-viewer/preset-office | PDF、Word、Excel、PPT、OFD、RTF、OpenDocument |
| 工程资料 | @file-viewer/preset-engineering | CAD、3D、XMind、Typst、绘图、Geo、Archive、EDA、Data |
| 全格式附件中心 | @file-viewer/preset-all | 需要完整格式矩阵的附件平台和 Demo |
| 极致裁剪 | 单独安装 @file-viewer/renderer-* | 只看 PDF、只看 Word、只看 CAD 等单链路业务 |
只接 PDF 的最小示例
npm i @file-viewer/vue3 @file-viewer/core @file-viewer/vite-plugin @file-viewer/renderer-pdf
// vite.config.ts
import { defineConfig } from 'vite'
import { fileViewerRenderers } from '@file-viewer/vite-plugin'
export default defineConfig({
plugins: [
fileViewerRenderers({
formats: ['pdf'],
copyAssets: true,
chunkStrategy: 'renderer'
})
]
})
import { configuredFileViewerRenderers } from 'virtual:file-viewer-renderers'
const options = {
builtinRenderers: 'none',
rendererMode: 'replace',
renderers: configuredFileViewerRenderers
}
这类配置适合合同系统、票据系统、审批系统里只关心 PDF 的页面。
办公文档平台示例
npm i @file-viewer/vue3 @file-viewer/core @file-viewer/vite-plugin @file-viewer/preset-office
import { fileViewerRenderers } from '@file-viewer/vite-plugin'
export default {
plugins: [
fileViewerRenderers({
preset: 'office',
scan: true,
copyAssets: true,
chunkStrategy: 'renderer'
})
]
}
import { configuredFileViewerRenderers } from 'virtual:file-viewer-renderers'
const options = {
builtinRenderers: 'none',
rendererMode: 'replace',
renderers: configuredFileViewerRenderers,
theme: 'light',
toolbar: { position: 'bottom-right' }
}
preset-office 更适合 OA、合同、知识库、文档中心这类系统。
全格式体验示例
npm i @file-viewer/vue3 @file-viewer/core @file-viewer/preset-all
import { allRenderers } from '@file-viewer/preset-all'
const options = {
builtinRenderers: 'none',
renderers: allRenderers
}
如果你正在做一个“企业附件中心”,想让用户在一个入口里打开尽可能多的格式,preset-all 会更省心。
跨框架组件生态
File Viewer 的一个重要变化是:各框架组件都围绕 core 实现原生体验,不需要把 Vue 组件塞给 React 用,也不需要把 iframe 当成默认集成方式。
| 技术栈 | 推荐包 | 说明 |
|---|---|---|
| Pure Web / script 标签 | @file-viewer/web | <flyfish-file-viewer>、IIFE、mountViewer、资源复制 CLI |
| Vue 3 | @file-viewer/vue3 | Vue 插件、组件、事件、ref/controller |
| Vue 2.7 | @file-viewer/vue2.7 | Vue 2.7 项目平滑接入 |
| Vue 2.6 | @file-viewer/vue2.6 | Vue 2.6 老项目专线 |
| React 18/19 | @file-viewer/react | React 组件、hooks、ref handle |
| React 16.8/17 | @file-viewer/react-legacy | 旧 React 项目兼容入口 |
| jQuery | @file-viewer/jquery | 传统后台插件式接入 |
| Svelte | @file-viewer/svelte | Svelte 组件和 action |
Vue 3 使用示例
import { createApp } from 'vue'
import FileViewer from '@file-viewer/vue3'
import '@file-viewer/vue3/dist/file-viewer3.css'
import App from './App.vue'
createApp(App).use(FileViewer).mount('#app')
<template>
<section class="preview-page">
<file-viewer
ref="viewer"
url="/files/report.pdf"
:options="options"
@load-complete="onLoadComplete"
/>
</section>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { FileViewerExpose } from '@file-viewer/vue3'
import { configuredFileViewerRenderers } from 'virtual:file-viewer-renderers'
const viewer = ref<FileViewerExpose | null>(null)
const options = {
theme: 'light',
builtinRenderers: 'none',
rendererMode: 'replace',
renderers: configuredFileViewerRenderers,
toolbar: { position: 'bottom-right' }
}
function onLoadComplete(context) {
console.log('预览完成:', context.filename)
}
</script>
React 使用示例
import FileViewer, { useFileViewer } from '@file-viewer/react'
import { configuredFileViewerRenderers } from 'virtual:file-viewer-renderers'
export function AttachmentPreview() {
const viewer = useFileViewer({
url: '/files/report.docx',
options: {
theme: 'light',
builtinRenderers: 'none',
renderers: configuredFileViewerRenderers,
toolbar: false
}
})
return (
<section style={{ height: '100vh' }}>
<button
disabled={!viewer.state.availability?.print}
onClick={() => viewer.handle.printRenderedHtml()}
>
打印
</button>
<FileViewer ref={viewer.ref} {...viewer.props} />
</section>
)
}
无构建工具 script 标签示例
<script src="/vendor/file-viewer-web/flyfish-file-viewer-web.iife.js"></script>
<flyfish-file-viewer
src="/files/demo.pdf"
filename="demo.pdf"
theme="light"
toolbar-position="bottom-right"
style="display:block;height:720px"
></flyfish-file-viewer>
传统后台、老系统、微前端壳都可以走这个方式。后续如果需要更强的状态控制,可以切换到 mountViewer(container, options)。
URL、File、Blob、ArrayBuffer 都能接
企业系统里文件来源经常不统一。一个项目里可能同时有对象存储 URL、鉴权下载接口、本地上传、加密文件、无扩展名接口。
File Viewer 推荐这样处理:
| 文件来源 | 推荐方式 |
|---|---|
| 静态 URL | 直接传 url,最好让 URL 带扩展名 |
| 鉴权接口 | 业务侧先 fetch,拿到 Blob 后包装成 File |
| 本地上传 | 直接传用户选择的 File |
| 解密文件 | 业务侧解密后传 ArrayBuffer,同时传文件名和类型 |
| 无扩展名 URL | 显式传 filename、name 或 type |
鉴权下载示例:
async function loadFileWithToken(id: string, token: string) {
const response = await fetch(`/api/files/${id}`, {
headers: {
Authorization: `Bearer ${token}`
}
})
const blob = await response.blob()
return new File([blob], 'contract.pdf', {
type: 'application/pdf'
})
}
无扩展名接口示例:
const options = {
filename: 'invoice.ofd',
type: 'ofd'
}
这一点非常重要。很多预览失败并不是组件不支持,而是业务接口只返回 application/octet-stream,文件名也没有扩展名,前端根本不知道该走哪个 renderer。
支持格式:不只 Office 和 PDF
下面按业务类型看一眼支持矩阵。
| 类别 | 代表格式 | 典型场景 |
|---|---|---|
| Word | docx、docm、dotx、dotm、doc、dot、rtf、odt | 公文、合同、模板、历史附件 |
| Excel | xlsx、xlsm、xlsb、xls、csv、ods、fods、numbers | 台账、报表、业务清单 |
| PowerPoint | pptx、pptm、potx、ppsx、odp | 课件、方案、演示材料 |
| PDF / OFD | pdf、ofd | 合同、票据、公文、归档 |
| Typst | typ、typst | 技术报告、论文草稿、工程模板 |
| CAD | dwg、dxf、dwf、dwfx、xps | 工程图纸、AutoCAD 归档 |
| 3D | glb、gltf、obj、stl、ply、fbx、dae 等 | 设计模型、点云、三维资产 |
| 绘图 | excalidraw、drawio、mermaid、plantuml | 架构图、流程图、白板图、UML |
| XMind | xmind | 脑图、规划图、知识结构 |
| 压缩包 | zip、7z、rar、tar、gz、jar、apk 等 | 归档附件、批量交付包 |
| 邮件 | eml、msg、mbox | 邮件归档、工单、客户来信 |
| EDA | olb、dra、gds、oas、oasis | 元件库、封装图纸、芯片版图初筛 |
| Markdown / 代码 | md、json、ts、vue、py、sql、patch、bundle 等 | 知识库、配置、日志、代码审阅 |
| 图片 / 媒体 | png、webp、heic、mp4、webm、m3u8、mp3、midi 等 | 图片、录屏、音频、流媒体 |
| 数据资产 | psd、sqlite、parquet、avro、wasm、ttf、woff2 等 | 设计资产、数据库、二进制资产 |
这里有几个值得单独展开的点。
Word:不是简单把内容吐出来
Word 预览最容易被低估。真实业务里的 Word 通常有标题层级、目录、表格、页眉页脚、图片、制表符、字段和复杂样式。
File Viewer 的现代 Word 链路由 @file-viewer/renderer-word 承接,DOCX 系列使用自研 @file-viewer/docx,强调可读性、连续阅读和复杂文档稳定展示。
适合:
- 合同
- 公文
- 说明书
- Word 模板
- 长文档归档预览
PDF:版式文件的稳定入口
PDF 适合合同、票据、审批单、正式归档材料。File Viewer 的 PDF 链路基于 PDF.js,支持页码、缩放、目录、导航窗格、宽度自适应、打印和导出。
同源 PDF 可以直接走 URL 渐进读取,服务端支持 Range 时体验会更好。
CAD:DWG、DXF、DWF 都能进入浏览器
CAD 文件预览过去通常很麻烦,要么服务端转图,要么依赖桌面软件。File Viewer 的 CAD 链路基于 @flyfish-dev/cad-viewer,支持 DWG、DXF、DWF、DWFx、XPS 等格式。
适合:
- 工程图纸系统
- 制造业附件中心
- 设计资料管理
- 图纸归档预览
XMind:脑图也可以在附件中心直接打开
XMind 文件常出现在产品规划、会议纪要、知识整理和项目管理中。File Viewer 通过独立 mindmap renderer 支持多 sheet、目录树、拖拽平移、缩放和适配视图。
Mermaid / PlantUML / Draw.io / Excalidraw
架构图、流程图、UML 图、白板图都是研发和产品团队常见附件。
这类格式适合:
- 技术方案归档
- 知识库架构图
- 需求流程图
- 团队协作文档
PSD、SQLite、Parquet、Avro 等数据资产
File Viewer 也覆盖了一些“不是传统文档,但业务里经常遇到”的文件。
例如 PSD 可以展示画布和图层:
SQLite、Parquet、Avro、WASM、字体文件等则更适合做结构预览和快速审阅。
文档比对:单独入口,不污染主预览
File Viewer 还提供了独立文档比对入口:
https://demo.file-viewer.app/compare.html
可以通过 URL 参数预置左右文档:
https://demo.file-viewer.app/compare.html?left=/example/word.docx&right=/example/pdf.pdf
这个页面适合:
- 合同修订对比
- 文档版本审阅
- Word / PDF 混合对照
- 资料归档前核对
能力包括:
- 左右并排预览
- 示例 / URL / 本地上传
- 同步滚动
- 聚焦搜索
- 行级定位
- PDF 工具栏隐藏
常见业务能力:水印、打印、下载、搜索、导出
企业系统接文件预览,通常不是“能看”就结束了。
File Viewer 把常见操作做成统一能力:
| 能力 | 说明 |
|---|---|
| 下载原文件 | 保留原始文件下载能力,可接权限校验 |
| 打印 | 按格式能力动态展示,Word / PDF 等走文档主体打印 |
| 导出 HTML | 导出渲染后的主体内容和必要样式 |
| 水印 | 支持文字或图片水印,可配透明度、旋转、间距 |
| 主题 | 支持 light、dark、system |
| 搜索 | 支持关键词高亮、上一个、下一个、清空 |
| 定位 | 支持行级定位、锚点定位、文本切片 |
| 前置校验 | 下载、打印、导出、缩放前可接权限或审计确认 |
水印示例:
const options = {
watermark: {
text: 'Internal Review',
opacity: 0.12,
rotate: -24,
gapX: 260,
gapY: 180
}
}
生命周期和权限校验示例:
const options = {
hooks: {
onLoadStart(context) {
console.log('开始预览', context.filename)
},
onLoadComplete(context) {
console.log('预览完成', context.filename, context.duration)
},
onUnloadComplete(context) {
console.log('文档卸载完成', context.reason)
}
},
async beforeOperation(context) {
if (context.operation === 'download') {
return await checkDownloadPermission(context.filename)
}
return true
},
toolbar: {
position: 'bottom-right',
async beforePrint(context) {
return await confirmPrint(context.filename)
}
}
}
搜索和 AI 友好文本结构示例:
await viewer.searchDocument('合同金额')
await viewer.nextSearchResult()
const chunks = await viewer.getDocumentTextChunks()
const anchors = await viewer.collectDocumentAnchors()
这类 API 可以给业务侧做:
- 审计溯源
- 搜索命中跳转
- 向量化入库
- AI 问答来源定位
- 合同条款定位
私有化部署:Worker / WASM / 字体 / vendor 资源怎么处理
复杂格式预览绕不开 Worker、WASM、字体和 vendor 静态资源。
例如:
| 链路 | 资源 |
|---|---|
| PDF.js worker、CMap、standard fonts | |
| CAD | LibreDWG WASM、DWF renderer、worker |
| Typst | compiler WASM、renderer WASM、字体资源 |
| SQLite | sql-wasm.wasm |
| Draw.io | diagrams.net 离线 viewer 资源 |
| Archive | libarchive worker 和 WASM |
Vite 项目推荐:
fileViewerRenderers({
preset: 'engineering',
copyAssets: true,
chunkStrategy: 'renderer'
})
Pure Web 或传统静态部署可以使用:
npx file-viewer-copy-assets ./public/file-viewer
内网部署时,建议重点确认:
- 静态资源路径是否与部署 base 一致
- WASM 是否有正确 MIME
- Worker 文件是否允许加载
- CSP 是否允许当前资源路径
- 大文件服务是否支持 Range
- 鉴权接口是否能提供文件名和类型
和后端转码方案怎么选
File Viewer 的定位是纯前端预览组件。它适合:
- 快速集成
- 前端控制体验
- 附件中心
- 内网部署
- 无需服务端 Office / LibreOffice 守护进程
- 多框架统一接入
如果你的业务要求:
- 极高保真 Office 排版
- 在线编辑
- 专业 CAD / EDA 几何校核
- DRM 或加密格式解析
- 超大文件服务端切片转换
那可以把 File Viewer 作为在线快速预览入口,把专业格式转换、编辑、校核放在服务端或专业软件链路里。这个边界讲清楚,项目选型会舒服很多。
我建议的接入路线
如果你准备在真实业务里落地,可以按这个顺序来:
- 先打开 demo.file-viewer.app,用真实业务文件试一遍。
- 看 doc.file-viewer.app/guide/forma…,确认格式边界。
- 按业务范围选择
preset-lite、preset-office、preset-engineering或单个 renderer。 - 用
@file-viewer/vite-plugin处理 renderer 装配和静态资源复制。 - 接入
theme、watermark、toolbar、hooks、beforeOperation。 - 给下载、打印、导出接业务权限和审计。
- 需要文档比对时,使用独立
/compare.html入口。 - 内网部署前,确认 Worker、WASM、字体、PDF.js、CAD、Typst、Archive 等静态资源路径。
总结
File Viewer 2.1 这次最值得关注的点,不只是“又多支持了一些格式”。
更关键的是,它把文件预览这件事拆成了一个更清晰的体系:
- core 负责底层能力
- renderer 负责具体格式
- preset 负责业务组合
- Vite 插件负责工程装配
- 各框架组件负责原生体验
对业务系统来说,这意味着:
- 可以从只看 PDF 的轻量接入开始
- 也可以升级成全格式附件中心
- 可以走 Vue / React / Svelte / jQuery / Pure Web
- 可以部署在公网,也可以放进企业内网
- 可以支持常规 Office,也可以覆盖 CAD、XMind、Typst、邮件、压缩包、绘图、数据资产和工程文件
如果你的系统里“附件预览”已经开始从一个小按钮变成一个大坑,File Viewer 值得认真试一下。
项目入口: