Skip to content

组件用法

API At A Glance

`file-viewer` 的 API 非常克制,目前真正需要记住的是两条输入路径:`url` 和 `file`,以及一个可选的 `options`。 但要把它接进真实业务里,光知道“有这两个参数”还不够,你还得知道渲染器是怎么识别文件类型的、什么时候该传 URL、什么时候应该先把结果包装成带扩展名的 `File`。

这套 API 在多个 npm 包中保持一致: Vue3 使用 @flyfish-group/file-viewer3@1.0.22,Vue2.7 使用 @flyfish-group/file-viewer@1.0.22,React 使用 @flyfish-group/file-viewer-react@1.0.22,纯 JS 使用 @flyfish-group/file-viewer-web@1.0.22。React 和纯 JS 包只负责 iframe、参数和二进制推送,默认加载私有化静态目录 /file-viewer/index.html

Vue3 和 Vue2 的安装器都会自动带上组件样式,不需要额外引入 CSS。

先记住这 4 条规则

  • 渲染器靠文件扩展名选择处理链路,所以文件名本身非常重要。
  • fileurl 同时存在时,组件会优先渲染 file
  • 如果你拿到的是 BlobArrayBuffer,推荐先包装成带扩展名的 File 再传入。
  • 组件会默认撑满父容器,所以父容器必须有明确高度。
  • 同源 PDF URL 默认交给 PDF.js 渐进读取,首屏不再等待外层预览器整包 Blob 下载;文件服务支持 Range 时会自动分片加载,跨域 URL 默认仍走兼容下载链路。
  • React、纯 JS 和 iframe 模式默认使用 /file-viewer/index.html,如果静态目录不同,请显式传入 viewerUrl

输入方式怎么选

输入方式推荐程度适合场景说明
url推荐文件地址可直接访问、链路简单同源 PDF 会直接交给 PDF.js 渐进读取;其他格式会在浏览器内下载文件,再按扩展名选择渲染器
file: File强烈推荐本地上传、鉴权下载后预览、宿主系统已拿到文件对象最稳妥的二进制接入方式
Blob / ArrayBuffer先包装再用SDK 返回二进制、接口已返回文件流建议先包装成 new File([...], 'demo.pdf'),把文件名和扩展名补全

React、纯 JS 和 iframe 适配层允许直接传 BlobArrayBuffer,但仍然需要同时提供 name,例如 contract.pdf。底层会把二进制推送给私有化 viewer iframe,渲染规则仍由 Vue3 基线预览器决定。

行为规则

  • 预览器会根据文件名扩展名自动选择渲染器
  • fileurl 同时存在时,优先渲染 file
  • file 被清空后,如果 url 仍然存在,会自动回退到 url
  • 组件默认撑满父容器,因此父容器必须有稳定高度
  • 扩展名匹配会自动转成小写,所以 PDFDocX 这类大小写差异不会影响命中
  • OFD、Typst、压缩包、邮件、OLB/DRA、CAD、3D 模型、绘图、EPUB、UMD、PDF、Office、Markdown、音频和代码高亮等渲染器均按需异步加载,只有命中文件类型时才拉取对应代码块
  • PPTX 属于浏览器端近似渲染链路,已增强组合图形、主题背景、图片裁剪和 EMF 矢量图片;如果业务材料大量使用复杂动画或专有 Office 特效,建议把真实样本加入上线前回归。
  • options 可以配置内置操作栏、水印、压缩包 Worker、缓存和体积上限。

URL 预览

vue
<script setup lang="ts">
import { ref } from 'vue'

const url = ref('https://example.com/demo.pdf')
</script>

<template>
  <div style="height: 100vh">
    <file-viewer :url="url" />
  </div>
</template>

适合场景:

  • 文件地址可直接访问
  • 文件服务已正确配置 CORS
  • 业务无需先进行额外下载或鉴权处理
如果你的 URL 形如 /download?id=123,路径里没有明确扩展名,预览器就很难准确判断该走哪条渲染链路。遇到这种带签名、带鉴权或通过接口中转的地址,建议直接先把结果取回来,再包装成 File 传入。

文件对象预览

vue
<script setup lang="ts">
import { ref } from 'vue'

const file = ref<File | undefined>()

function onChange(event: Event) {
  const input = event.target as HTMLInputElement
  const value = input.files?.item(0)
  if (value) file.value = value
}
</script>

<template>
  <div style="height: 100vh">
    <input type="file" @change="onChange" />
    <div style="height: calc(100vh - 40px)">
      <file-viewer :file="file" />
    </div>
  </div>
</template>

适合场景:

  • 用户本地上传后立即预览
  • 宿主系统已经拿到了最终文件对象
  • 文件访问链路涉及鉴权,不方便直接暴露 URL

鉴权接口返回 Blob 时怎么接

很多业务系统真正拿到的不是公开 URL,而是一个需要携带登录态的下载接口。这种情况下,推荐你先把 Blob 包成 File:

ts
const response = await fetch('/api/preview/contract/123', {
  credentials: 'include'
})

const blob = await response.blob()

file.value = new File([blob], 'contract.pdf', {
  type: blob.type
})

这样做的好处是:

  • 文件内容已经在宿主系统的权限链路里拿到了
  • 预览器可以通过 contract.pdf 正确识别扩展名
  • 业务侧可以自己决定缓存、重试和错误提示策略

SDK 返回 ArrayBuffer 时怎么接

如果你的下载 SDK 返回的是 ArrayBuffer,思路也一样,先补一个正确的文件名:

ts
const buffer = await sdk.downloadAttachment(id)

file.value = new File([buffer], 'report.xlsx')

这里最重要的不是 ArrayBuffer 本身,而是你给它补上的 report.xlsx 这个文件名。没有扩展名,渲染器就不知道该走哪条链路。

预览器 options

options 用于配置通用交互和重型格式的运行参数。Vue2 / Vue3 组件、React 组件、纯 JS helper 和 iframe 查询参数都使用同一套语义。

vue
<script setup lang="ts">
import { ref } from 'vue'

const url = ref('/example/archive.zip')
const options = {
  theme: 'light',
  toolbar: {
    position: 'bottom-right',
    download: true,
    print: true,
    exportHtml: true
  },
  watermark: {
    text: '内部资料',
    opacity: 0.16,
    rotate: -24,
    color: '#1f7a58'
  },
  archive: {
    workerUrl: '/vendor/libarchive/worker-bundle.js',
    cache: true,
    maxArchiveSize: 320 * 1024 * 1024,
    maxEntryPreviewSize: 64 * 1024 * 1024
  },
  pdf: {
    streaming: 'same-origin',
    rangeChunkSize: 64 * 1024
  }
}
</script>

<template>
  <div style="height: 100vh">
    <file-viewer :url="url" :options="options" />
  </div>
</template>
选项说明
theme预览器主题,支持 lightdarksystem。默认 system,继续跟随浏览器 prefers-color-scheme;浅色业务系统建议显式传 light,避免操作系统深色模式把预览区、工具栏或支持主题切换的渲染器自动切成深色
toolbartrue 或对象;声明是否允许下载原文件、打印完整渲染结果和导出渲染后 HTML。toolbar.position 支持 autotopbottom-right,默认 auto,PDF 会自动悬浮到右下角以避开自身导航栏,其他格式保持顶部。打印按钮还会结合当前文件类型、渲染完成状态和导出适配器动态显隐,Excel 等虚拟表格链路会隐藏打印按钮
watermarktrue、文字配置或图片配置;支持 textimageopacityrotategapX/gapYwidth/height、字体和颜色
archive.workerUrllibarchive.js Worker 地址;私有化部署时建议把 worker-bundle.jslibarchive.wasm 放在同一目录
archive.cache是否使用 IndexedDB 缓存已解压的压缩包内文件
archive.maxArchiveSize单个压缩包允许读取目录的最大体积,默认 320MB
archive.maxEntryPreviewSize压缩包内单文件允许预览的最大体积,默认 64MB
pdf.streamingPDF URL 渐进读取策略,默认 same-origin;设为 true 时跨域也尝试 URL 直连读取,设为 false 时完全回到 Blob 下载后预览
pdf.rangeChunkSizePDF.js Range 请求分片大小,默认 64KB;仅在文件服务支持 Range 时生效
pdf.withCredentialsPDF.js URL 读取是否携带浏览器凭据,默认 false

图片水印可以传 https URL、相对路径或 data URL。开启图片水印时,文字水印不会重复绘制。

Typst 文件通过 .typ / .typst 扩展名识别。组件会直接读取 Typst 源文件,并在命中格式时按需加载浏览器 WASM 编译器和 SVG 渲染链路;不会自动探测、替换或优先使用同名 PDF。默认 compiler WASM 使用固定 CDN 地址,也可以按私有化部署要求指定自己的地址。

ts
const options = {
  typst: {
    compilerWasmUrl: 'https://cdn.example.com/typst/typst_ts_web_compiler_bg.wasm'
  }
}

当前浏览器端编译更适合单文件 Typst 源文档;如果文档依赖同目录图片、字体或拆分源码,建议在业务侧先打包成压缩包预览,保留完整项目结构后再选择内部 .typ 文件。

性能敏感的 PDF 建议使用同源静态地址,并让文件服务支持 Range 请求。这样 PDF.js 可以先建立页面结构,再按需渲染当前页和附近页面;不支持 Range 时仍会走 PDF.js URL 渐进读取,避免外层预览器重复整包缓冲。鉴权接口、无后缀下载接口或跨域签名 URL 则建议继续由业务侧取回 Blob 后包装为 File,稳定性更高。

生命周期钩子和按钮前置校验

Vue2 / Vue3 组件可以直接通过 options.hooks 接收文档生命周期。每个回调都会拿到同一套上下文: typefilenamesourceurlfilesizeversiontimestamp,加载完成时还会带上 duration

ts
const options = {
  hooks: {
    onLoadStart(context) {
      console.log('开始加载', context.type, context.filename)
    },
    onLoadComplete(context) {
      console.log('加载完成', context.duration)
    },
    onUnloadStart(context) {
      console.log('开始卸载', context.reason)
    },
    onUnloadComplete(context) {
      console.log('卸载完成', context.filename)
    }
  },
  async beforeOperation(context) {
    if (context.operation === 'print') {
      return await checkCanPrint(context.filename)
    }
    return true
  },
  toolbar: {
    position: 'bottom-right',
    download: true,
    print: true,
    exportHtml: true,
    beforeDownload(context) {
      return confirm(`下载 ${context.filename}?`)
    }
  }
}

内置操作当前包括 downloadprintexport-htmloptions.beforeOperation 是全局前置钩子,toolbar.beforeOperation 会在工具栏层统一执行,toolbar.beforeDownload / toolbar.beforePrint / toolbar.beforeExportHtml 可以对单个按钮做精确控制。任意钩子返回 false 都会取消本次操作。预览器还会在文件切换、渲染完成和能力变化时抛出 operation-availability-change,宿主可以用它同步外部操作按钮。

React、纯 JS 和 iframe 集成无法把函数序列化进 iframe 查询参数,但可以通过事件监听拿到同样的生命周期和操作上下文。纯 JS 使用 onEvent,React 使用 onViewerEvent

ts
mountViewerFrame(container, {
  url: '/files/demo.pdf',
  onEvent(event) {
    if (event.type === 'flyfish-viewer:lifecycle') {
      console.log(event.event, event.payload)
    }
  }
})

打印、导出和水印的交付行为

  • 下载原文件会保留用户传入的原始二进制内容,不会把渲染后的页面反向写回文件。
  • 打印会生成只包含预览内容和水印的独立打印窗口,不带 Demo 侧边栏、示例选择器或操作工具条。
  • PDF 打印和导出 HTML 使用 PDF 专属导出适配器逐页生成完整页面,和当前滚动位置、当前可见页、导航窗格显隐状态都解耦,避免只输出当前页或被滚动容器截断。
  • Word 打印和导出会清理预览阶段的缩放、绝对定位、滚动容器和 Demo 全局布局样式,把 .docx / .doc 还原成完整白色页面,避免只打印当前视口或第一页。
  • 图片、Markdown、代码、PPTX、OFD、CAD、绘图、UMD、OLB/DRA 等可以稳定克隆当前渲染结果的格式会保留打印按钮;Excel 当前使用 styled-exceljs + e-virt-table 虚拟渲染,完整工作表不会一次性存在于 DOM 中,因此表格、压缩包、邮件、EPUB、音视频、3D / 模型等更适合交互查看或原文件下载的格式会隐藏打印按钮。
  • 导出 HTML 会尽量克隆当前渲染结果,并把 canvas 转成图片,保证图纸、绘图、文档和代码在离线 HTML 中仍有可读内容。
  • 水印会同时参与预览、打印和 HTML 导出。文字水印适合内部资料、审批流和归档场景;图片水印适合品牌 Logo 或业务系统标识。

典型切换方式

vue
<script setup lang="ts">
import { ref } from 'vue'

const url = ref('https://example.com/demo.docx')
const file = ref<File | undefined>()

function useRemote() {
  file.value = undefined
  url.value = 'https://example.com/demo.docx'
}

async function useLocal(blob: Blob) {
  file.value = new File([blob], 'local-preview.pdf', { type: blob.type })
}
</script>

这一组行为在组件里是稳定的:

  • file 一旦有值,就优先走 file
  • file 清空后,如果 url 还在,就会自动回退到 url

常见注意事项

父容器高度

这类问题最容易被忽略。如果父容器没有高度,预览器会跟着塌陷,最终看起来像“没有渲染”。

URL 请求失败

如果控制台里能看到 403、404 或 CORS 报错,问题一般不在预览器本身,而是在目标文件地址的可访问性上。

文件名要尽量准确

预览器依赖文件扩展名选择渲染器,所以无论你传入的是 URL 还是二进制结果,文件名都应该尽量带上正确扩展名。

OFD、Typst、压缩包、邮件、EDA、CAD、3D 模型、绘图和电子书怎么接

.ofd 会使用 DLTech21/ofd.js 仓库源码在浏览器端解析,避开 npm dist 的授权 wasm 分支。.dxf 会使用 CAD 预览器显示图纸。DWG 会先识别误命名 DXF,再尝试提取真实 DWG 的内嵌预览图;完整几何解析仍建议在业务侧转换为 DXF,避免把 GPL 或闭源 DWG 解析运行时打入组件包。

.typ / .typst 会直接读取源文件并加载 Typst WASM 编译和 SVG 渲染链路,组件会按 Typst 输出的页面元数据拆页显示。当前更适合单文件 Typst 文档;如果文档依赖外部图片、字体或拆分源码,建议用压缩包保留项目结构。

.zip.7z.rar.tar.gz.xz.cab.iso.jar.apk.cbz.cbr 等压缩包会使用 libarchive.js Worker 读取目录。内部文件在点击后按需解压,并继续交给对应格式预览器。私有化部署时请确认 /vendor/libarchive/worker-bundle.js 和同目录下的 libarchive.wasm 可访问,或者通过 options.archive.workerUrl 指定自己的静态地址。

.eml 使用 postal-mime.msg 使用 @kenjiuno/msgreader。邮件正文会在安全 iframe 中展示,附件可以下载,也可以继续在线预览。

.olb.dra 使用 cfb 做 OrCAD / Allegro 常见复合文档结构预览。组件会展示结构树、流类型、元件符号、封装、Padstack、属性、可读字符串和诊断信息;它适合附件初筛和内容确认,不替代专业 EDA 软件里的封装编辑、规则校核和电气验证。

3D 模型使用 Three.js,支持 glb/gltf/obj/stl/ply/fbx/dae/3ds/3mf/amf/usd/usda/usdc/usdz/kmz/pcd/wrl/vrml/xyz/vtk/vtp。如果模型有外部贴图、材质或 .bin,远程 url 预览会按原始文件目录继续加载;本地上传时更推荐使用单文件 .glbstep/stp/iges/igs/ifc/3dm 会给出需要 CAD/BIM/WASM 几何内核的原因和转换建议。

.excalidraw 会使用官方 @excalidraw/excalidrawexportToSvg 生成只读 SVG 预览;.drawio / .dio 会使用官方 diagrams.net GraphViewer 渲染,不在组件里手写 mxGraphModel 解析逻辑。

.epub 会使用 epubjs 解析电子书包、目录和章节资源,并在浏览器内提供只读滚动阅读。阅读器会默认打开第一个正文章节,避免停留在封面或空白包装页。

.umd 会按早期移动电子书结构在浏览器端解析文件头、元数据、章节偏移、章节标题和压缩正文。正文数据块使用 pako 解压并按 UTF-16LE 解码,适合历史小说附件和旧移动阅读文件。Kindle 专有格式或 DRM 电子书建议先转换为 EPUB / UMD 文本电子书 / PDF 后再传入预览器。

音频怎么接

.mp3.mpeg.wav.ogg.oga.opus.m4a.aac.flac.weba 会走浏览器原生 <audio> 播放器。不同浏览器对音频编码支持不完全一致,如果要保证最稳的跨端体验,建议优先输出 MP3 或 OGG。

html 会被当网页渲染吗

不会。html 在当前版本属于代码/文本类型,会按源码内容高亮显示,而不是作为真正网页执行。这一层策略更安全,也更适合做代码、模板和片段查看。

Released under the Apache-2.0 License.