概述
在企业级应用开发场景下,非常常见的一个需求就是多格式文档的预览,包括但不限于:TXT、CSV、PDF、Word、PPT、Excel等。通常情况下,这些文件会上传到MinIO、阿里云OSS或腾讯云COS。
根据个人知识所限,MinIO只能预览TXT、CSV、PDF,并不能预览Office三件套,更别谈其他各种各样的文件格式。
那商业化文件存储平台呢?
OSS
亲自尝试,阿里云OSS预览Word文档,需绑定IMM智能媒体项目
点击【绑定】
点击【授权】
可惜,OSS免费开通,IMM需付费使用;本着写技术博客的严谨精神,还是开通体验一下
点击桶(Bucket)内的.docx文件,自动下载线性代数.htm文件,浏览器打开此文件,加载几分钟还出来。
商业平台能力,就这样????
另外,再说一个很搞笑的Bug(非常明显的问题):
Bucket列表为空,创建一个preview-test的桶,结果提示我已存在,我他妈为啥要关心这个名称被其他用户占用???OSS作为SaaS应用,而SaaS最基本的概念就是多租户,不同用户创建相同的Bucket名称,有什么问题吗?
上面说到有2个方案,方案1是图片方案,猜测是基于截图来实现,限于时间因素,未经验证。有空补上。
关于IMM的介绍:可绑定智能媒体管理产品,来做数据分析预处理
- 调用方式:OSS域名/sample_object?x-oss-process=imm/action,param_value
- 接口实例:OSS域名/family.jpg?x-oss-process=imm/detectface
IMM基于x-oss-process推出全新的文档处理、音视频处理等能力,支持样式、异步任务、批处理、触发器等多种使用方式。
COS
上传文件后,有【预览】入口
点击预览,果不其然,也是收费服务
点击【开启服务】
预览效果 
开源
商业化平台能力是ok的,但涉及到企业内部安全隐私,数据泄密等问题,最好还是使用本地化部署方案。本文收集几个基于浏览器的文件预览开源项目。
kkFileView
官网,一个基于Java+Spring Boot的万能文件预览开源(GitHub,14.4K Star,3.3K Fork)项目,允许用户在浏览器端实时查看文件内容。
支持的文件类型:
极易上手,使用HTTP接口访问,不需要和应用集成,具有跨系统跨语言使用的特性。提供Windows/Linux版本发行包、自定义配置文件、和一键启动/停止脚本等,极大方便部署使用,官方提供Docker镜像。
因此仅需单独将kkFileView部署好后,文件交由minio存储和维护,然后提供文件的url地址既可实现在线预览,如下图所示:
部署
git clone https://github.com/kekingcn/kkFileView.git
cd kkfileviewer
docker build -t kkfileview:v5.0.0 .
docker run -d -p 8012:8012 –name kkfileview kkfileview:v5.0.0
打开:http://localhost:8012,开始体验。
集成MinIO
添加依赖:implementation 'io.minio:minio:8.5.17'
application.yaml配置:
minio:
endpoint: http://xxxxx:9000
accessKey: xxxx
secretKey: xxxxx
bucketName: test
MinIO配置类:
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endPoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Value("${minio.bucketName}")
private String bucketName;
@Bean
protected MinioClient minioClient(){
return MinioClient.builder()
.endpoint(endPoint)
.credentials(accessKey, secretKey)
.build();
}
}
工具类:
@Component
public class MinioUtils {
@Value("${minio.bucketName}")
private String bucketName;
@Resource
private MinioClient minioClient;
public ApiResult uploadFile(MultipartFile file) throws AppException {
String fileName = System.currentTimeMillis() + file.getOriginalFilename();
try (InputStream fi = file.getInputStream()) {
PutObjectArgs putObjectArgs = PutObjectArgs.builder().bucket(bucketName).contentType(file.getContentType()).object(fileName).stream(fi, fi.available(), –1).build();
minioClient.putObject(putObjectArgs);
} catch (Exception e) {
throw new AppException("文件上传失败" + e.getMessage());
}
return ApiResult.ok(fileName);
}
public ApiResult getPreviewUrl(String objectName) throws AppException {
try {
GetPresignedObjectUrlArgs urlArgs = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).method(Method.GET).build();
return ApiResult.ok(minioClient.getPresignedObjectUrl(urlArgs));
} catch (Exception e) {
throw new AppException("获取预览链接失败" + e.getMessage());
}
}
}
controller接口类:
@RestController
@RequestMapping("/file")
public class FileOperationController {
@Resource
private MinioUtils minioUtils;
@PostMapping("/upload")
public ApiResult upload(MultipartFile file) throws AppException {
return minioUtils.uploadFile(file);
}
@GetMapping("/getPreviewUrl")
public ApiResult getPreviewUrl(String fileName) throws AppException {
return minioUtils.getPreviewUrl(fileName);
}
}
File Viewer
官网,全能、纯前端、开源(GitHub,1.8K Star,185 Fork)商用友好的文件预览器。基于浏览器运行,覆盖Office、PDF、OFD、Typst、CAD、3D、压缩包、邮件、绘图、电子书、Markdown、代码、图片、音频、视频等常见业务附件。官方文档,在线Demo。
| 全能纯前端文件预览器 | 文件在浏览器端完成解析和展示,业务系统可用一套组件覆盖绝大多数附件预览需求 |
| 完全开源,Apache-2.0 | 协议商用友好,适合企业内部系统、SaaS产品、低代码平台和二开集成 |
| 覆盖几乎所有常见附件场景 | 合同、档案、工单、知识库、工程图纸、邮箱附件、代码审阅、培训资料、压缩包归档都能进入统一预览体验 |
| 优质且高效的代码链路 | 按文件类型异步加载重型依赖,PDF.js、CAD WASM、docx-preview、epub.js、libarchive等只在需要时进入页面 |
| 可扩展性完整清晰 | @file-viewer/core负责底层预览能力,Vue、React、纯Web、jQuery、Svelte等组件入口保持统一参数、事件和操作语义 |
适用场景
| 合同审阅 | Word/PDF/OFD+水印+打印+下载前置校验+文档比对 |
| 档案归档 | PDF/OFD/Office+生命周期回调+搜索定位+HTML导出 |
| 邮箱附件归档 | EML/MSG/MBOX+附件继续预览+原文下载 |
| 工程图纸 | DWG/DXF/DWF/DWFx/XPS+CAD WASM私有化+图层/结构面板 |
| 知识库 | Markdown/代码/PDF/Word+全文搜索+文本切片+来源定位 |
| 低代码平台 | Pure Web/Vue/React组件+统一options+动态文件源 |
| SaaS中后台 | 主题固定为light+权限前置操作+统一工具栏 |
| 内网部署 | 静态资源自托管+Docker/静态目录+Worker/WASM路径配置 |
核心架构
| @file-viewer/core作为纯TypeScript底座 | 格式矩阵、渲染协议、生命周期、搜索、缩放、打印、导出、水印等能力集中沉淀,框架层更轻 |
| 多生态标准组件拆分 | Vue、React、纯Web、jQuery、Svelte等入口都只依赖core,参数和事件语义保持一致 |
| 渲染链路按需异步加载 | 命中文件类型后才加载PDF.js、CAD WASM、docx-preview、epubjs、libarchive等重型依赖 |
| Demo、文档、Release、静态部署体系完整 | 官网、文档站、在线Demo、文档比对页、Docker/静态产物、npm包和Release下载物形成完整分发闭环 |
开发者可从最小组件开始接入,再逐步打开水印、搜索、打印、权限前置校验、WASM私有化、文档比对和AI溯源等能力。
扩展名支持参考packages/core/src/formats.ts,并由pnpm verify:format-support校验:194个扩展名映射,23条预览链路。不包含内部错误兜底处理器。
| Word/模板 | docx、docm、dotx、dotm、doc、dot | docx-preview、msdoc-viewer | 合同、制度、报告、历史Word附件。复杂版式优先保持页面阅读效果 |
| Excel/表格 | xlsx、xlsm、xlsb、xls、csv、ods、fods、numbers | styled-exceljs+虚拟表格 | 业务台账、导出报表、多sheet文件。表格链路以交互查看为主 |
| PowerPoint | pptx、pptm、potx、ppsx | @aiden0z/pptx-renderer | 课件、方案、汇报材料。浏览器近似渲染,复杂动画建议转PDF留档 |
| PDF.js | 合同、归档件、说明书。支持页导航、目录、缩放、搜索、打印和HTML导出 | ||
| OFD | ofd | DLTech21/ofd.js | 发票、政企归档。按需加载OFD解析链路 |
| Typst | typ、typst | Typst WASM+SVG | 直接预览Typst源文件,适合技术文档和排版稿 |
| CAD/图纸 | dwg、dxf、dwf、dwfx、xps | @flyfish-dev/cad-viewer+WASM/Worker | 工程图纸、设备图、DWF/DWFx/XPS审阅。私有部署需配置WASM/Worker路径 |
| 3D模型 | glb、gltf、obj、stl、ply、fbx、dae、3mf、usd等 | Three.js | 产品模型、工程资产、简单三维附件。复杂CAD/BIM可先转换 |
| 压缩包 | zip、7z、rar、tar、gz、xz、iso、jar、apk、cbz等 | libarchive.js Worker+兼容降级 | 查看压缩包目录,按需解压内部文档并继续预览 |
| 邮件 | eml、msg、mbox | postal-mime、msgreader | 邮件归档、客服工单、附件审阅。正文隔离渲染,附件可继续预览 |
| 绘图 | excalidraw、drawio、dio | 官方Excalidraw/diagrams.net链路 | 流程图、草图、架构图,只读预览 |
| 电子书 | epub、umd | epubjs、UMD parser | 内部手册、电子书、历史移动阅读文件 |
| Markdown/代码 | md、json、js、ts、tsx、vue、py、java、sql、log等 | Markdown+highlight.js | 知识库、配置、源码、日志,安全地按源码展示 |
| 图片/设计资产 | png、jpg、svg、webp、heic、psd、ai、eps | 浏览器图片、按需解析器 | 品牌素材、截图、设计附件。AI/EPS做安全摘要或PDF-backed预览 |
| 音视频 | mp3、ogg、flac、midi、mp4、webm、m3u8 | 原生媒体、hls.js、MIDIparser | 通话录音、培训视频、媒体附件 |
| 数据资产 | sqlite、parquet、avro、wasm、webarchive、字体文件 | sql.js、hyparquet、avsc、FontFace | 快速查看结构、表、schema、导入导出信息 |
组件生态围绕core提供多条生产可用入口。新项目推荐优先使用标准包名,历史包名继续保持兼容。
技术栈
| Core | @file-viewer/core | 无 | 自研组件、底层能力、格式识别、生命周期和操作API |
| Vue 3 | @file-viewer/vue3 | @flyfish-group/file-viewer3、file-viewer3 | Vue3中后台、知识库、合同系统 |
| Vue 2.7 | @file-viewer/vue2.7 | @flyfish-group/file-viewer | 存量Vue2.7项目 |
| Vue 2.6 | @file-viewer/vue2.6 | 无 | 老Vue2.6项目 |
| React 18/19 | @file-viewer/react | @flyfish-group/file-viewer-react | React中后台、SaaS、设计系统 |
| React 16.8/17 | @file-viewer/react-legacy | 无 | 老React项目 |
| 纯Web/JS | @file-viewer/web | @flyfish-group/file-viewer-web | 原生页面、Web Components外壳、微前端、script标签 |
| jQuery | @file-viewer/jquery | 无 | 老后台、CMS、传统页面 |
| Svelte | @file-viewer/svelte | 无 | Svelte应用和组件库 |
对于独立静态预览站,可把自托管Demo页面放入业务iframe做隔离展示。新业务集成建议优先使用上面的原生组件或mountViewer,调试体验和事件回调会更直接。
实战
安装:
pnpm add @file-viewer/vue3
# React、纯Web、jQuery、Svelte分别安装对应包
pnpm add @file-viewer/react
pnpm add @file-viewer/web
pnpm add @file-viewer/jquery
pnpm add @file-viewer/svelte
Vue3示例:
<script setup lang="ts">
import FileViewer from '@file-viewer/vue3'
</script>
<template>
<div style="height: 100vh">
<FileViewer url="/files/demo.pdf" />
</div>
</template>
Vue组件会自动带上样式,父容器需要明确高度,这是所有预览器最容易被忽略的基础条件。
React示例:
import FileViewer from '@file-viewer/react'
export function Preview() {
return (
<div style={{ height: '100vh' }}>
<FileViewer url="/files/contract.docx" />
</div>
)
}
纯JS/Script标签:
import { mountViewer } from '@file-viewer/web'
const controller = mountViewer(document.getElementById('viewer')!, {
url: '/files/demo.pdf',
options: {
theme: 'light'
}
})
传统页面也可使用IIFE产物,通过file-viewer-copy-assets把viewer assets发布到自己的静态目录,再用全局对象挂载。
URL最简单,适合文件地址能被浏览器直接访问的场景:
<FileViewer url="https://cdn.example.com/report.pdf" />
本地上传、鉴权下载和SDK返回二进制时,推荐传File:
constfile =newFile([blob],'contract.pdf', {
type: blob.type
})
ArrayBuffer也建议先包装成带扩展名的File:
const buffer = await sdk.downloadAttachment(id)
const file = new File([buffer], 'report.xlsx')
文件名很关键,预览器会根据扩展名选择渲染链路。遇到/download?id=123这种无后缀地址,推荐业务侧先下载,再补一个准确文件名。
鉴权文件下载推荐方式
const response = awaitfetch('/api/files/123/preview', {
credentials: 'include',
headers: {
Authorization: `Bearer ${token}`
}
})
const blob = await response.blob()
const file = newFile([blob], 'sealed-contract.ofd', {
type: blob.type
})
这样权限仍然由业务系统控制,预览器只负责浏览器端解析和展示。
CAD、压缩包、PDF、Typst、SQLite、DOCX Worker等链路会按需使用Worker或WASM。内网、CSP严格、CDN子路径部署时,可显式配置资源地址:
const options = {
pdf: {
workerUrl: '/file-viewer/vendor/pdf/pdf.worker.mjs',
streaming: 'same-origin',
rangeChunkSize: 64 * 1024
},
cad: {
wasmPath: '/file-viewer/wasm/cad/',
workerUrl: '/file-viewer/wasm/cad/dwg-worker.js',
dwfWasmUrl: '/file-viewer/wasm/cad/dwfv-render.wasm'
},
archive: {
workerUrl: '/file-viewer/vendor/libarchive/worker-bundle.js',
workerTimeoutMs: 30000,
cache: true
},
typst: {
compilerWasmUrl: '/file-viewer/wasm/typst/typst_ts_web_compiler_bg.wasm',
rendererWasmUrl: '/file-viewer/wasm/typst/typst_ts_renderer_bg.wasm'
},
data: {
sqlWasmUrl: '/file-viewer/wasm/sql/sql-wasm.wasm'
}
}
加载、卸载、打印、下载、导出、缩放都可接入业务审计和权限控制。
const options = {
hooks: {
onLoadStart(ctx) {
console.log('开始加载', ctx.type, ctx.filename)
},
onLoadComplete(ctx) {
console.log('加载完成', ctx.duration)
},
onUnloadStart(ctx) {
console.log('开始卸载', ctx.reason)
},
onUnloadComplete(ctx) {
console.log('卸载完成', ctx.filename)
}
},
asyncbeforeOperation(ctx) {
if (ctx.operation === 'print') {
returnawaitcheckPrintPermission(ctx.filename)
}
returntrue
}
}
水印、打印、下载、导出HTML
const options = {
theme: 'light',
toolbar: {
position: 'bottom-right',
download: true,
print: true,
exportHtml: true,
zoom: true
},
watermark: {
text: '内部资料',
opacity: 0.16,
rotate: –24,
color: '#0f766e'
}
}
打印会尽量只输出主体文档,不带Demo外壳。PDF和Word会按页面尺寸处理;Excel等虚拟表格链路会根据能力动态隐藏打印按钮,避免输出只有当前视口。
预览器提供searchDocument()、nextSearchResult()、previousSearchResult()、collectDocumentAnchors()、scrollToLine()、scrollToAnchor()和getDocumentTextChunks()等能力。
业务侧可把文本切片写入自己的向量库或审计系统,再通过锚点回到原文位置。File Viewer 本身只提供结构、定位和高亮能力,不绑定任何云端模型。
文档比对是独立页面,适合合同版本、PDF与源文件、PPTX版本差异、Markdown与导出稿对照:https://demo.file-viewer.app/compare.html
可预置左右文件:
/compare.html?left=/example/pdf.pdf&right=/example/word.docx
网硕互联帮助中心


评论前必须登录!
注册