前端文件预览别再开盲盒了:Flyfish File Viewer 支持 206+ 格式,纯前端全能预览还模块化

1 阅读13分钟

前端文件预览别再开盲盒了:Flyfish File Viewer 2.1 支持 206+ 格式,纯前端全能预览还模块化

File Viewer 2.1 封面 如果你在企业系统里做过“附件预览”,大概率见过这种场景:

产品说:“就是点一下看看文件,很简单吧?”

然后用户上传了:

  • 一个 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-officePDF、Word、Excel、PPT、OFD、RTF、OpenDocument
工程资料@file-viewer/preset-engineeringCAD、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/vue3Vue 插件、组件、事件、ref/controller
Vue 2.7@file-viewer/vue2.7Vue 2.7 项目平滑接入
Vue 2.6@file-viewer/vue2.6Vue 2.6 老项目专线
React 18/19@file-viewer/reactReact 组件、hooks、ref handle
React 16.8/17@file-viewer/react-legacy旧 React 项目兼容入口
jQuery@file-viewer/jquery传统后台插件式接入
Svelte@file-viewer/svelteSvelte 组件和 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显式传 filenamenametype

鉴权下载示例:

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

在这里插入图片描述

下面按业务类型看一眼支持矩阵。

类别代表格式典型场景
Worddocxdocmdotxdotmdocdotrtfodt公文、合同、模板、历史附件
Excelxlsxxlsmxlsbxlscsvodsfodsnumbers台账、报表、业务清单
PowerPointpptxpptmpotxppsxodp课件、方案、演示材料
PDF / OFDpdfofd合同、票据、公文、归档
Typsttyptypst技术报告、论文草稿、工程模板
CADdwgdxfdwfdwfxxps工程图纸、AutoCAD 归档
3Dglbgltfobjstlplyfbxdae设计模型、点云、三维资产
绘图excalidrawdrawiomermaidplantuml架构图、流程图、白板图、UML
XMindxmind脑图、规划图、知识结构
压缩包zip7zrartargzjarapk归档附件、批量交付包
邮件emlmsgmbox邮件归档、工单、客户来信
EDAolbdragdsoasoasis元件库、封装图纸、芯片版图初筛
Markdown / 代码mdjsontsvuepysqlpatchbundle知识库、配置、日志、代码审阅
图片 / 媒体pngwebpheicmp4webmm3u8mp3midi图片、录屏、音频、流媒体
数据资产psdsqliteparquetavrowasmttfwoff2设计资产、数据库、二进制资产

这里有几个值得单独展开的点。

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导出渲染后的主体内容和必要样式
水印支持文字或图片水印,可配透明度、旋转、间距
主题支持 lightdarksystem
搜索支持关键词高亮、上一个、下一个、清空
定位支持行级定位、锚点定位、文本切片
前置校验下载、打印、导出、缩放前可接权限或审计确认

水印示例:

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 静态资源。

例如:

链路资源
PDFPDF.js worker、CMap、standard fonts
CADLibreDWG WASM、DWF renderer、worker
Typstcompiler WASM、renderer WASM、字体资源
SQLitesql-wasm.wasm
Draw.iodiagrams.net 离线 viewer 资源
Archivelibarchive 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 作为在线快速预览入口,把专业格式转换、编辑、校核放在服务端或专业软件链路里。这个边界讲清楚,项目选型会舒服很多。

我建议的接入路线

如果你准备在真实业务里落地,可以按这个顺序来:

  1. 先打开 demo.file-viewer.app,用真实业务文件试一遍。
  2. doc.file-viewer.app/guide/forma…,确认格式边界。
  3. 按业务范围选择 preset-litepreset-officepreset-engineering 或单个 renderer。
  4. @file-viewer/vite-plugin 处理 renderer 装配和静态资源复制。
  5. 接入 themewatermarktoolbarhooksbeforeOperation
  6. 给下载、打印、导出接业务权限和审计。
  7. 需要文档比对时,使用独立 /compare.html 入口。
  8. 内网部署前,确认 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 值得认真试一下。

项目入口: