做后台系统的人,迟早都会碰到一个需求:
附件在线预览。
一开始看着很简单,图片直接展示,PDF 接个 pdf.js,文本塞进代码编辑器,视频交给浏览器。
真正麻烦的是后面。
用户开始上传 doc、xls、pptx、wps、et,再混进压缩包、邮件、图纸、代码文件,原本一个小功能,很快就变成一套独立系统。
我自己也被这个问题折腾过很多次。
所以我做了一个文件预览 SDK:
Open File Viewer。
目前已经支持原生 JavaScript、Vue、React、Svelte,Office 方向也覆盖了 doc、docx、xls、xlsx、pptx 等常见格式。
这篇文章不讲概念,直接聊它解决了什么,以及这套 SDK 是怎么设计的。
文件预览最难的地方,是失控
很多项目最早的预览代码,大概都是这样:
if (isPdf(file)) {
openPdf(file);
} else if (isImage(file)) {
openImage(file);
} else if (isOffice(file)) {
openOffice(file);
} else {
download(file);
}
刚开始没问题。
等格式一多,业务就会慢慢失控:
PDF 有一套工具栏
图片有一套缩放逻辑
Office 有一套加载状态
视频有自己的控制器
压缩包只能下载
错误状态各写各的
同一个预览功能,弹窗里写一遍,详情页再写一遍,审批流和附件中心继续复制。
最后项目里没有“文件预览能力”,只有一堆散落的格式判断。
我开发 Open File Viewer,最先解决的就是这个问题:
把所有文件预览统一收口到一个入口。
业务层只负责传文件,剩下的格式识别、插件匹配、渲染、错误处理、下载兜底,全部交给 SDK。
一套 Core,支持四种技术栈
Open File Viewer 没有把能力绑死在某个框架里。
它的核心结构分成几层:
JavaScript / Vue / React / Svelte
↓
Framework Adapter
↓
Open File Viewer Core
↓
Plugin System
↓
Renderer
底层 Core 负责:
文件识别
插件调度
状态管理
容器生命周期
工具栏
主题
错误处理
Fallback
上层的 Vue、React、Svelte 只负责框架适配。
所以不管项目用什么技术栈,底层走的都是同一套预览能力。
一个团队里完全可能同时存在:
老后台:原生 JavaScript
管理端:Vue
新产品:React
轻量页面:Svelte
以前需要分别维护四套方案,现在可以共用一套插件和渲染逻辑。
这也是我一开始就把它做成 SDK,而不是只做一个 Vue 组件的原因。
插件系统才是核心
文件格式太多,靠一个超级组件硬扛,后面一定会越来越重。
所以 Open File Viewer 把不同格式拆成独立插件。
比如:
const plugins = [
officePlugin(),
pdfPlugin(),
imagePlugin(),
textPlugin()
];
文件传入之后,内部流程大致是:
读取文件信息
↓
识别扩展名和 MIME
↓
匹配对应插件
↓
创建 Renderer
↓
渲染到统一容器
每个插件只处理自己负责的内容。
officePlugin() 关注 Office,pdfPlugin() 处理 PDF,imagePlugin() 负责图片,后面也可以继续扩展音视频、压缩包、CAD、GIS、3D 等能力。
这种结构有两个好处。
第一,格式之间互不干扰。Office 插件出问题,不会影响图片和 PDF。
第二,业务可以按需加载。项目只需要 PDF 和 Office,就只注册这两个插件,不必把所有能力全部打进包里。
doc / xls 支持,才是真正进入业务系统
很多 Office 预览方案都会写一句:
支持 Word、Excel、PPT。
真正点进去一看,支持的是:
docx
xlsx
pptx
碰到 doc、xls 就没了。
但在真实项目里,老版 Office 文件根本绕不开。
特别是这些系统:
OA
档案管理
合同系统
财务系统
政企项目
内部知识库
历史数据里往往有大量 doc 和 xls。
用户不会区分 doc 和 docx,也不会关心二进制格式和 XML 格式的区别。他只知道这个文件明明是 Word,为什么点不开。
所以这次我把 doc、xls 也补了进来。
目前 Office 方向覆盖:
doc
docx
xls
xlsx
pptx
rtf
odt
csv
wps
et
dps
对国内业务来说,wps、et、dps 同样很重要。
文件格式从来不会按照开发者最舒服的方式出现,预览组件能不能处理这些“脏数据”,直接决定了它能不能落地。
四种技术栈,接入方式保持一致
原生 JavaScript
import {
OpenFileViewer,
officePlugin,
pdfPlugin,
imagePlugin
} from"@open-file-viewer/core";
const viewer = new OpenFileViewer({
container: document.querySelector("#viewer"),
plugins: [
officePlugin(),
pdfPlugin(),
imagePlugin()
]
});
viewer.open(file);
原生项目、老后台、WebView、低代码平台都可以直接使用,不需要额外引入框架。
Vue
<OpenFileViewer
:file="file"
:file-name="file.name"
:plugins="plugins"
width="100%"
height="640px"
toolbar
/>
React
<OpenFileViewer
file={file}
fileName={file.name}
plugins={plugins}
width="100%"
height="640px"
toolbar
/>
Svelte
<OpenFileViewer
{file}
fileName={file.name}
{plugins}
width="100%"
height="640px"
toolbar
/>
四套写法看起来不同,底层调用的是同一个 Core。
这意味着格式支持、工具栏、异常状态、插件能力都不会因为框架不同而割裂。
真正难做的,还有失败状态
文件预览不可能永远成功。
真实项目里经常会碰到:
文件链接过期
对象存储跨域
文件体积过大
格式伪装
文件损坏
浏览器能力不足
插件加载失败
所以一套能落地的预览 SDK,必须把完整状态链路处理掉:
loading
preview
error
unsupported
download fallback
Open File Viewer 把这些状态统一放进容器里。
业务层不用给每一种格式重复写加载动画、错误提示和下载按钮。
预览成功,就进入 Renderer。
预览失败,就进入 Fallback。
格式不支持,也可以直接提示下载,或者接入服务端转换。
Office 高保真预览,必须保留服务端路线
浏览器端预览 Office 有天然边界。
复杂 Word 里可能有:
特殊字体
页眉页脚
浮动图片
文本框
修订记录
嵌套表格
Excel 里也会出现复杂公式、图表、冻结窗格和大数据量。
前端可以完成基础预览,但很难百分之百复刻 Microsoft Office 或 WPS 的排版结果。
所以更稳的工程方案通常是:
普通文件
前端直接预览
复杂文件
服务端转换为 PDF
前端继续预览
Open File Viewer 可以把两条路线统一起来。
前端能直接处理的,交给 Office 插件;需要高保真的,服务端转成 PDF,再交给 PDF 插件。
用户看到的仍然是同一个预览容器,业务页面也不用维护两套 UI。
官网已经可以在线体验
我做了一个在线官网,可以直接体验:https://open-file-viewer-workspace.void.app/
里面可以查看不同格式的预览效果,也可以了解插件体系和接入方式。
正在做这些项目的朋友,可以重点试一下:
附件中心
OA 审批
ERP 单据
CRM 合同
知识库
网盘
低代码平台
档案系统
企业文档平台
这些系统看起来完全不同,底层碰到的问题却高度一致:
文件类型杂,预览逻辑乱,后期维护成本高。
Open File Viewer 想做的,就是把这部分能力统一沉淀下来。
完全免费开源,求个 Star
项目目前完全免费开源。
GitHub 地址:
https://github.com/xushanpei/open-file-viewer
我开发这个项目的原因很简单:
希望把自己踩过的坑做成一套通用能力,帮助更多开发者少重复造轮子。
文件预览这种项目,表面上没有多复杂,真正做起来全是细节。
每一种格式、每一种异常状态、每一个浏览器差异、每一条 Fallback 路径,都需要一点点补齐。
如果你正在做文件预览,欢迎直接拿去试。
觉得项目有用,也希望能顺手给个 Star。
开源不易,一个 Star,真的会成为我继续维护下去的动力。
官网地址:
https://open-file-viewer-workspace.void.app/GitHub 地址:
https://github.com/xushanpei/open-file-viewernpm 地址:
https://www.npmjs.com/package/@open-file-viewer/core