一 背景
公司制作在线文件预览库,可接受不能预览doc、ppt;
1)图片采用:element-plus的 el-image-viewer
2)vue-office 系列组件可预览 :pdf、docx、excel(xls、xlsx、csv)、pptx
3)bestofdview:预览ofd
4)dplayer 预览音频:mp4,这别后台会校验mp4 前 200 KB 内 有 moov(可边加载边播放)
其他介绍
txt:直接读取内容:放入网页显示(本文章不做介绍)
html:不推荐预览,加载script 可能会攻击你的网站
这里档案库禁止 doc、ppt 上传,除了上面能预览的档案外,档案库还允许上传安全的压缩包类型;
注:为了保证文件的防止传播性,这边后台会为每次预览生成一个临时token,且预览地址会携带该参数;后台会校验token 合法、短时间有效、甚至是单次有效,防止通过预览地址下载到原文件
1.1 预览效果
1 图片:注:不一定要点击图片显示,也可点击按钮直接渲染:el-image-viewer 组件
地址中图片预览 ,https://element-plus.org/zh-CN/component/image
2 pdf、office系列
地址中的:演示效果,https://github.com/501351981/vue-office
3 ofd
地址中的:在线预览地址,https://github.com/besthqs/bestofdview
4 媒体
地址中的媒体:https://github.com/DIYgod/DPlayer
1.2 安装依赖
# 预览图片
pnpm install element-plus
# 预览pdf
pnpm install @vue-office/pdf
# 预览 docx
pnpm install @vue-office/docx
# 预览 xls、xlsx
pnpm install @vue-office/excel
# 预览ppt
pnpm install @vue-office/pptx
# 预览 ofd
pnpm install bestofdview
# 预览mp4
pnpm install dplayer
二 实现介绍
2.1 图片
element-plus 版本 2.9.9 ,组件名imageDialog
效果,地址中的:图片预览 ,https://element-plus.org/zh-CN/component/image
注:不一定要点击图片显示,也可点击按钮直接渲染:el-image-viewer 组件
<template>
<el-watermark v-if="imgViewerVisible" :content="watermarkContent" :font="watermarkFont " :z-index="3100"
style="position: fixed; inset: 0; z-index: 3000">
<el-image-viewer
:url-list="[fileUrl]"
:initial-index="0"
:z-index="3000"
@close="closeDialog"
/>
</el-watermark>
</template>
<script setup>
const
// 图片预览是否显示
const imgViewerVisible = ref(false)
// 文件url
const fileUrl = ref()
const watermarkContent = ref('水印')
const watermarkFont = ref({
color: 'rgba(0, 0, 0, 0.2)',
fontSize: 16,
fontWeight: 'bold',
fontFamily: 'sans-serif'
})
// 关闭窗口
const closeDialog = () => {
dialogVisible.value = false;
imgViewerVisible.value = false;
fileUrl.value = '';
}
const open = async (url ) => {
fileUrl.value = url
imgViewerVisible.value = true
})
2.2 pdf预览
PdfView.vue 官方样例:https://github.com/501351981/vue-office
注:options 传空即可,这里禁用多线程加载是为了保护服务器
<template>
<vue-office-pdf
:src="fileUrl"
style="flex: 1;height: 0"
:options="options"
@rendered="rendered" @error="handleError"
/>
</template>
<script setup>
//引入VueOfficePdf组件
import VueOfficePdf from '@vue-office/pdf'
const emit = defineEmits(['error'])
const props = defineProps({
fileUrl: {
type: String,
default: undefined
}
})
let options = {
// 禁用 range
disableRange: true,
// 禁用流式加载
disableStream: true,
// 禁用自动预取
disableAutoFetch: true,
}
const rendered = () => {
console.log("渲染完成")
}
const handleError = (error) => {
emit('error', error)
}
</script>
2.3 docx预览
DocView.vue 官方样例:https://github.com/501351981/vue-office
<template>
<vue-office-docx
:src="fileUrl"
style="flex: 1;height: 0"
@rendered="rendered" @error="handleError"
/>
</template>
<script setup>
//引入VueOfficeDocx组件
import VueOfficeDocx from '@vue-office/docx'
//引入相关样式
import '@vue-office/docx/lib/index.css'
const emit = defineEmits(['error'])
const props = defineProps({
fileUrl: {
type: String,
default: undefined
}
})
const rendered = () => {
console.log("渲染完成")
}
const handleError = (error) => {
emit('error', error)
}
</script>
2.4 xlxs、csv 预览
ExcelView.vue 官方样例:https://github.com/501351981/vue-office
<template>
<vue-office-excel
:src="fileUrl"
:options="{xls: xls}"
style="height: 100vh;"
@rendered="rendered" @error="handleError"
/>
</template>
<script setup>
//引入VueOfficeExcel组件
import VueOfficeExcel from '@vue-office/excel'
//引入相关样式
import '@vue-office/excel/lib/index.css'
const emit = defineEmits(['error'])
const props = defineProps({
fileUrl: {
type: String,
default: undefined
},
// 是否是xls
xls: {
type: Boolean,
default: false
}
})
const rendered = () => {
console.log("渲染完成")
}
const handleError = (error) => {
emit('error', error)
}
</script>
2.5 pptx 预览
PptxView.vue 官方样例:https://github.com/501351981/vue-office
<template>
<vue-office-pptx
:src="fileUrl"
style="flex: 1;height: 0"
@rendered="rendered" @error="handleError"
/>
</template>
<script setup>
//引入VueOfficePptx组件
import VueOfficePptx from '@vue-office/pptx'
const emit = defineEmits(['error'])
const props = defineProps({
fileUrl: {
type: String,
default: undefined
}
})
const rendered = () => {
console.log("渲染完成")
}
const handleError = (error) => {
emit('error', error)
}
</script>
2.6 ofd 预览
OfdView.vue 官方样例:https://github.com/besthqs/bestofdview
<template>
<div class="ofd-view-container">
<OfdView
:showOpenFileButton="true"
:ofdLink="fileUrl"
></OfdView>
</div>
</template>
<script setup>
import { OfdView } from "bestofdview";
import "bestofdview/dist/style.css";
const props = defineProps({
fileUrl: {
type: String,
default: undefined
}
})
</script>
<style scoped>
.ofd-view-container {
width: 100%;
height: 80vh;
}
</style>
2.7 dplayer 预览
VideoPlay.vue 官方样例:https://github.com/DIYgod/DPlayer
<template>
<div>
<div id="dplayer" style="height: 80vh"></div>
</div>
</template>
<script setup>
import {onMounted} from "vue";
import DPlayer from 'dplayer';
const props = defineProps({
fileUrl: {
type: String,
default: undefined
}
})
onMounted(()=>{
const dp = new DPlayer({
container: document.getElementById('dplayer'),
screenshot: true,
video: {
url: props.fileUrl,
}
});
})
</script>
<style scoped></style>
三 防止通过预览下载的媒体的方式
# 1 前端改进:
地址是传递的方法:每次预览器访问地址会生成1个新token 预览地址
# 2 后端校验
1)后台对资源预览禁用缓存:防止用户直接新页签打开走缓存直接保存文件
response.setHeader("Cache-Control", "no-store");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");
2)校验是通过安全域名,访问本文件
String referer = request.getHeader("Referer");
referer.contains("网站域名")
3)token 合法校验
token 放入 redis,校验存在,访问1次就删除;且校验token用户在登录用户,且token未过期
4)每次访问记录日志,记录访问的ip,用于追溯