前端解决方案 - office 系列文件预览
- 简介
- 微软 Office Web Viewer
-
- 前端实现
- pdf 预览
-
- 本地选择
- 远程请求
- docx 预览
-
- 本地选择
- 远程请求
- xlsx 预览
-
- 本地选择
- 远程请求
- vue-office 组件
-
- pdf预览
- docx预览
- xlsx预览
简介
如果遇到文档管理类的业务功能,会出现需要在线预览的业务需求,合同文件、发票文件等业务同样需要文件的在线预览。
本文围绕以下解决方案展开:
微软 Office Web Viewer
pdfjs
docx-preview
xlsx
第三方封装组件
微软 Office Web Viewer
Office Web Viewer 是由微软提供的一项免费服务,用户不需要安装 Office 软件,直接通过浏览器在线预览 docx、xlsx、pptx 文件。
Office Web Viewer 的服务链接为:
https://view.officeapps.live.com/op/view.aspxsrc=[文件链接]
其中 [文件链接] 是需要预览文件的在线地址,按需替换成自己的文件链接即可。
预览 docx
预览 xlsx
预览 pptx
注意
1、文件会传输到微软的服务器上,因此可能会涉及到文件隐私。
2、不支持 pdf 预览。
适用场景
- 用于公网展示的文件预览,如某公告文件在线预览,能快速实现需求。
- 支持 docx、xlsx、pptx 文件,用于无需下载文件直接在线预览,适用于分享查看。
前端实现
出于通用性考虑,通过 encodeURIComponent 对特殊字符进行编码
<script setup lang="ts">
import { ref } from 'vue'
// 预览地址
const officeUrl = ref('')
// 资源地址
const docx = 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.docx'
const xlsx = 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.xlsx'
const pptx = 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.pptx'
const pdf = 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.pdf'
// 使用微软 Office Web Viewer 服务预览 office 文件
const previewOffice = (url: string) => {
// 考虑到特殊字符,通过 encodeURIComponent 处理一下 url
officeUrl.value = `https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)}`
}
// 复制分享地址
const copyToClipboard = async (text: string) => {
await navigator.clipboard.writeText(text)
alert('复制成功')
}
</script>
<template>
<div class="container">
<div class="btns">
<!-- 微软 Office Web Viewer 服务 -->
<button @click="previewOffice(docx)">预览docx</button>
<button @click="previewOffice(xlsx)">预览xlsx</button>
<button @click="previewOffice(pptx)">预览pptx</button>
<!-- pdf 直接预览 -->
<button @click="officeUrl = pdf">预览pdf</button>
<!-- 复制分享地址 -->
<button class="copy-btn" v-if="officeUrl" @click="copyToClipboard(officeUrl)">
复制分享地址
</button>
</div>
<!-- 通过 iframe 嵌入 -->
<iframe class="previewOffice" :src="officeUrl"></iframe>
</div>
</template>
<style lang="scss">
body {
margin: 0;
}
.container {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
.previewOffice {
flex: 1;
}
.copy-btn {
background-color: #070;
color: #fff;
}
}
</style>
pdf 预览
通过 PDF.js 加载 PDF 文件,渲染到 Canvas 上。
- 安装依赖 npm install pdfjs-dist
- 设置 workerSrc 的值
官方教程
npm install pdfjs-dist
业务场景
渲染 pdf 的需求较为常见,如预览电子发票,合同,学术论文等。
本地选择
<script setup lang="ts">
import { ref } from 'vue'
import * as pdfjsLib from 'pdfjs-dist'
// 设置 workerSrc 的值,cdn 远程加载
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.js`
const canvasRef = ref<HTMLCanvasElement>()
const previewPdf = async (event: Event) => {
// 获取文件
const file = (event.target as HTMLInputElement).files?.[0]
// 获取 canvas
const canvas = canvasRef.value
if (file && canvas) {
const ctx = canvas.getContext('2d')
if (!ctx) return
const data = await file.arrayBuffer()
pdfjsLib.getDocument({ data }).promise.then((pdf) => {
pdf.getPage(1).then((page) => {
const viewport = page.getViewport({ scale: 1.5 })
canvas.width = viewport.width
canvas.height = viewport.height
page.render({ canvasContext: ctx, viewport })
})
})
}
}
</script>
<template>
<!-- pdf 预览 -->
<input type="file" accept=".pdf" @change="previewPdf" />
<br />
<canvas ref="canvasRef"></canvas>
</template>
远程请求
<script setup lang="ts">
import * as pdfjsLib from 'pdfjs-dist'
import { ref } from 'vue'
import axios from 'axios'
// 设置 workerSrc 的值,cdn 远程加载
pdfjsLib.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.js`
const canvasRef = ref<HTMLCanvasElement>()
const file = ref()
const getFile = async () => {
const res = await axios({
method: 'get',
url: 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.pdf',
responseType: 'arraybuffer',
})
file.value = res.data
}
const previewFile = async () => {
await getFile()
const canvas = canvasRef.value
if (file.value && canvas) {
const ctx = canvas.getContext('2d')
if (!ctx) return
const data = new Uint8Array(file.value)
pdfjsLib.getDocument({ data }).promise.then((pdf) => {
pdf.getPage(1).then((page) => {
const viewport = page.getViewport({ scale: 1.5 })
canvas.width = viewport.width
canvas.height = viewport.height
page.render({ canvasContext: ctx, viewport })
})
})
}
}
</script>
<template>
<button @click="previewFile">获取文档</button>
<br />
<!-- pdf 预览容器 -->
<canvas ref="canvasRef"></canvas>
</template>
docx 预览
通过 docx-preview 加载 docx 文件。
- 安装依赖
- 调用 renderAsync 方法来渲染文档
官方文档
npm install docx-preview
本地选择
<script setup lang="ts">
import { ref } from 'vue'
import { defaultOptions, renderAsync } from 'docx-preview'
// 定义一个 ref 来存储容器元素
const container = ref<HTMLElement>()
// 定义一个异步函数 previewFile 来预览文档
const previewFile = async (event: Event) => {
// 获取上传的文件
const file = (event.target as HTMLInputElement).files?.[0]
if (file && container.value) {
// 将文件转换为 ArrayBuffer
const arrayBuffer = await file.arrayBuffer()
// 调用 renderAsync 方法来渲染文档
await renderAsync(arrayBuffer, container.value, undefined, {
...defaultOptions,
className: 'docx',
inWrapper: true,
ignoreWidth: false,
ignoreHeight: false,
ignoreFonts: false,
breakPages: true,
ignoreLastRenderedPageBreak: true,
experimental: false,
trimXmlDeclaration: true,
debug: false,
})
}
}
</script>
<template>
<div>
<!-- 添加一个文件上传的 input 元素,绑定 change 事件到 previewFile 函数 -->
<input type="file" accept=".docx" @change="previewFile" />
<!-- 添加一个 div 元素,通过 ref 绑定到 container 变量 -->
<div ref="container"></div>
</div>
</template>
远程请求
<script setup lang="ts">
import { ref } from 'vue'
import { defaultOptions, renderAsync } from 'docx-preview'
import axios from 'axios'
// 定义一个 ref 来存储容器元素
const container = ref<HTMLElement>()
const fileRef = ref()
const getFile = async () => {
const res = await axios({
method: 'get',
url: 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.docx',
// 返回类型为 arraybuffer
responseType: 'arraybuffer',
})
fileRef.value = res.data
}
// 定义一个异步函数 previewFile 来预览文档
const previewFile = async () => {
// 获取文档
await getFile()
// 获取 arraybuffer
const file = fileRef.value
if (file && container.value) {
// 调用 renderAsync 方法来渲染文档
await renderAsync(file, container.value, undefined, {
...defaultOptions,
className: 'docx',
inWrapper: true,
ignoreWidth: false,
ignoreHeight: false,
ignoreFonts: false,
breakPages: true,
ignoreLastRenderedPageBreak: true,
experimental: false,
trimXmlDeclaration: true,
debug: false,
})
}
}
</script>
<template>
<button @click="previewFile">获取文档</button>
<br />
<!-- 添加一个 div 元素,通过 ref 绑定到 container 变量 -->
<div ref="container"></div>
</template>
xlsx 预览
通过 xlsx 加载 xlsx 文件。
- 安装依赖
- 调用相关方法获取 excel 文件数据
官方教程
npm install xlsx
本地选择
<script setup lang="ts">
import { ref } from 'vue'
import { read, utils } from 'xlsx'
// 本地选择文件
const rows = ref<any>([])
const previewFile = async (event: Event) => {
// 获取上传的文件
const file = (event.target as HTMLInputElement).files?.[0]
if (file) {
// 将文件转换为 ArrayBuffer
const arrayBuffer = await file.arrayBuffer()
// 读取 Sheets
const sheet = read(arrayBuffer).Sheets
/* 渲染数据 */
rows.value = utils.sheet_to_json(sheet['Data'])
}
}
</script>
<template>
<input type="file" accept=".xlsx" ref="fileInput" @change="previewFile" />
<table>
<thead>
<th>Name</th>
<th>Index</th>
</thead>
<tbody>
<tr v-for="(item, index) in rows" :key="index">
<td>{{ item.Name }}</td>
<td>{{ item.Index }}</td>
</tr>
</tbody>
</table>
</template>
远程请求
<script setup lang="ts">
import axios from 'axios'
import { ref } from 'vue'
import { read, utils } from 'xlsx'
const getFile = async () => {
const res = await axios({
method: 'get',
url: 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.xlsx',
// 返回类型为 arraybuffer
responseType: 'arraybuffer',
})
return res.data
}
const rows = ref<any>([])
const previewFile = async () => {
const arrayBuffer = await getFile()
if (arrayBuffer) {
// 读取 Sheets
const sheet = read(arrayBuffer).Sheets
/* 渲染数据 */
rows.value = utils.sheet_to_json(sheet['Data'])
}
}
</script>
<template>
<button @click="previewFile">获取文档</button>
<br />
<table>
<thead>
<th>Name</th>
<th>Index</th>
</thead>
<tbody>
<tr v-for="(item, index) in rows" :key="index">
<td>{{ item.Name }}</td>
<td>{{ item.Index }}</td>
</tr>
</tbody>
</table>
</template>
vue-office 组件
支持多种文件(docx、excel、pdf)预览的 vue 组件库。
- 同时支持 vue2/3
- 使用简单
- 支持本地文件
- 支持远程地址
官方文档
pdf预览
安装依赖
npm install @vue-office/pdf
导入并使用
<script setup lang="ts">
// 引入 VueOffice 组件
import VueOfficePdf from '@vue-office/pdf'
import { ref } from 'vue'
// 设置文档网络地址,可以是本地文件
const src = ref()
// 本地预览
const previewFile = async (event: Event) => {
src.value = (event.target as HTMLInputElement).files?.[0]
}
// 请求预览
const getFile = () => {
src.value = 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.pdf'
}
</script>
<template>
<button @click="getFile">获取远程</button>
<input type="file" accept=".pdf" @change="previewFile" />
<vue-office-pdf :src="src" style="height: 100vh" />
</template>
docx预览
安装依赖
npm install @vue-office/docx
导入并使用
```vue
<script setup lang="ts">
// 引入 VueOffice 组件
import VueOfficeDocx from '@vue-office/docx'
// 引入相关样式
import '@vue-office/docx/lib/index.css'
import { ref } from 'vue'
// 设置文档网络地址,可以是本地文件
const src = ref()
// 本地预览
const previewFile = async (event: Event) => {
src.value = (event.target as HTMLInputElement).files?.[0]
}
// 请求预览
const getFile = () => {
src.value = 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.docx'
}
</script>
<template>
<button @click="getFile">获取远程</button>
<input type="file" accept=".docx" @change="previewFile" />
<vue-office-docx :src="src" style="height: 100vh" />
</template>
xlsx预览
安装依赖
npm install @vue-office/excel
导入并使用
<script setup lang="ts">
// 引入 VueOffice 组件
import VueOfficeExcel from '@vue-office/excel'
// 引入相关样式
import '@vue-office/excel/lib/index.css'
import { ref } from 'vue'
// 设置文档网络地址,可以是本地文件
const src = ref()
// 本地预览
const previewFile = async (event: Event) => {
src.value = (event.target as HTMLInputElement).files?.[0]
}
// 请求预览
const getFile = () => {
src.value = 'https://yjy-teach-oss.oss-cn-beijing.aliyuncs.com/solution/demo.xlsx'
}
</script>
<template>
<button @click="getFile">获取远程</button>
<input type="file" accept=".xlsx" @change="previewFile" />
<vue-office-excel :src="src" style="height: 100vh" />
</template>