这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
前言
最近开发了一个建筑相关的移动端项目,其中有各种附件预览的功能。通过各种尝试,总结出了一些常用的文件预览方式。本文介绍vue3常用文件(pdf、word、excel、txt)的预览。
一、pdf预览
本文使用的pdf预览是通过 pdfjs-dist 插件,在 Vue3
使用的时候还是有一些坑。
- 首先通过
npm
安装插件
npm install --save pdfjs-dist
- 然后在页面中引入
const PDF = require('pdfjs-dist')
- 启动项目之后就会报错如下:
仔细查看报错信息,是插件中使用了 es11
的语法 ?.
然而当项目启动,插件中的代码没有经过编译,导致项目不能启动。解决方案是在项目中配置webpack
针对于这个插件进行编译。
修改项目根目录下 vue.config.js
,如果没有就创建一个,在文件中增加如下代码:
// vue.config.js
module.exports = {
// ...
chainWebpack: config => {
// ...
config.module.rule('pdfjs-dist').test({
test: /\.js$/,
include: path.join(__dirname, 'node_modules/pdfjs-dist')
}).use('babel-loader').loader('babel-loader').options({
presets: ['@babel/preset-env'],
plugins: ['@babel/plugin-proposal-optional-chaining']
})
}
}
- 之后的错误
需要在引入 pdfjs-dist
之后配置 workerSrc
,但是引入 pdfjs-dist/build/pdf.worker.entry
之后浏览器还是有个警告:Warning: Setting up fake worker.
,经过各种原因查找,最终找到了一句描述:pdf.worker.js必须位于自己的文件中(而不是与pdf.js捆绑在一起)。否则它不能在服务工作线程中运行。
解决方式:将 pdfjs-dist/build/pdf.worker.js
复制一份放到项目 public
目录下。
-
pdf解析组件代码
通过
pdfjs-dist
加载pdf文件,获取到总页数将canvas
遍历到页面上,然后编写一个递归函数通过getPage
解析 pdf 的每一页将pdf内容渲染到canvas
。以下示例就是封装了一个解析pdf组件,通过传递文件链接全屏渲染pdf。
<template>
<div class="pdf">
<template v-for="item in pageNum" :key="item">
<canvas :id="`pdf-canvas-${item}`" class="pdf-page" />
</template>
</div>
</template>
<script>
import { reactive, toRefs, nextTick, watchEffect } from 'vue'
const PDF = require('pdfjs-dist')
PDF.GlobalWorkerOptions.workerSrc = '/pdf.worker.js'
export default {
name: 'DisplayPdf',
props: {
url: {
type: String,
default: ''
}
},
setup (props, { emit }) {
const state = reactive({
pageNum: 0,
pdfCtx: null
})
watchEffect(() => {
if (props.url) {
resolvePdf(props.url)
}
})
const resolvePdf = (url) => {
const loadingTask = PDF.getDocument(url)
loadingTask.promise.then(pdf => {
state.pdfCtx = pdf
state.pageNum = pdf.numPages
nextTick(() => {
renderPdf()
})
})
}
const renderPdf = (num = 1) => {
state.pdfCtx.getPage(num).then(page => {
const canvas = document.getElementById(`pdf-canvas-${num}`)
const ctx = canvas.getContext('2d')
const viewport = page.getViewport({ scale: 1 })
// 画布大小,默认值是width:300px,height:150px
canvas.height = viewport.height
canvas.width = viewport.width
// 画布的dom大小, 设置移动端,宽度设置铺满整个屏幕
const clientWidth = document.body.clientWidth
canvas.style.width = clientWidth + 'px'
// 根据pdf每页的宽高比例设置canvas的高度
canvas.style.height = clientWidth * (viewport.height / viewport.width) + 'px'
page.render({
canvasContext: ctx,
viewport
})
if (num < state.pageNum) {
renderPdf(num + 1)
} else {
emit('onRendered')
}
})
}
return {
...toRefs(state)
}
}
}
</script>
二、txt文件预览
txt
的文件预览就比较简单了,因为没有样式,我们直接读取文件的内容,展示到页面即可。
需要注意的是,如果需要正常显示 txt
文件中的换行符需要在文本容器上加上样式:white-space: pre-wrap;
,或者将内容中所有的换行符替换成 <br>
使用 v-html
显示内容。
<template>
<div class="txt" style="white-space: pre-wrap;">{{ txtContent }}</div>
</template>
<script>
import { ref } from 'vue'
import axios from 'axios'
export default {
name: 'PreviewTxt',
setup () {
const txtContent = ref('')
const url = '/demo.txt'
axios.get(url, {
responseType: 'text'
}).then(res => {
txtContent.value = res.data
})
return {
txtContent
}
}
}
</script>
三、word预览
本文中使用的是 mammoth.js
,会忽略复杂样式,只是实现了简单内容预览,使用方式也很简单,通过http请求获取文件的 ArrayBuffer
,再使用 mammoth.convertToHtml
将内容转换成 html
。如果文件能够外网访问并且机密性不高,还是推荐使用 http://view.officeapps.live.com/op/view.aspx?src=文档url
的方式预览。
// npm install mammoth --save
<template>
<div class="word-container" v-html="doc" />
</template>
<script>
import { watchEffect, ref } from 'vue'
import axios from 'axios'
import mammoth from 'mammoth'
export default {
name: 'DisplayDocx',
props: {
url: {
type: String,
default: ''
}
},
setup (props) {
const doc = ref('')
const resolveDocx = async (url) => {
const { data } = await axios.get(url, { responseType: 'arraybuffer' })
const res = await mammoth.convertToHtml({ arrayBuffer: data })
doc.value = res.value
}
watchEffect(() => {
if (props.url) resolveDocx(props.url)
})
return { doc }
}
}
</script>
四、excel预览
excel预览是使用 xsls
插件,读取文件的某个 Sheet
中的内容,然后把内容转换成 json
我们自己用json渲染表格。
npm install xsls --save
读取出json内容如下,默认以第一行为 key。
也可以通过 sheet_to_json
第二个参数改变,如果传入 { header: 1 }
则返回每行为文件中的一行组成数组
示例代码
import xlsx from 'xlsx'
import axios from 'axios'
export default {
name: 'PreviewExcel',
async setup () {
const url = '/demo.xlsx'
const { data } = await axios.get(url, { responseType: 'arraybuffer' })
const ctx = xlsx.read(data, { type: 'array' })
const result = xlsx.utils.sheet_to_json(ctx.Sheets[ctx.SheetNames[0]], { header: 1 })
console.log(result)
}
}