前端解决方案 - office 系列文件预览

news2024/12/12 15:13:34
前端解决方案 - 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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2258298.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

爬虫基础之代理的基本原理

在做爬虫的过程中经常会遇到一种情况&#xff0c;就是爬虫最初是正常运行、正常抓取数据的&#xff0c;一切看起来都是那么美好&#xff0c;然而一杯茶的工夫就出现了错误&#xff0c;例如 403 Forbidden&#xff0c;这时打开网页一看&#xff0c;可能会看到“您的IP访问频率太…

数据结构——对顶堆

对顶堆 由一个大根堆和一个小根堆组成&#xff0c;小根堆里面的数永远比大根堆里面的数要大 用途&#xff1a;用于动态维护区间内第k大的数&#xff0c;要比线段树和动态平衡树写起来更简单 比如说我们要维护第k大的数&#xff0c;那么我们肯定是将前k大的数放进小根堆&#…

设计模式之原型模式:深入浅出讲解对象克隆

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 原型模式概述 在我们的日常生活中&#xff0c;经常会遇到"复制"这样的场景。比如我们在准备文件时&#xff0c;常常会复印一份原件&a…

Elasticsearch Serverless 中的数据流自动分片

作者&#xff1a;来自 Elastic Andrei Dan 在 Elastic Cloud Serverless 中&#xff0c;我们根据索引负载自动为数据流配置最佳分片数量&#xff0c;从而使用户无需摆弄分片。 传统上&#xff0c;用户会更改数据流的分片配置&#xff0c;以处理各种工作负载并充分利用可用资源。…

【Golang】Go语言编程思想(六):Channel,第四节,Select

使用 Select 如果此时我们有多个 channel&#xff0c;我们想从多个 channel 接收数据&#xff0c;谁来的快先输出谁&#xff0c;此时应该怎么做呢&#xff1f;答案是使用 select&#xff1a; package mainimport "fmt"func main() {var c1, c2 chan int // c1 and …

MindSearch深度解析实践

任务要求&#xff1a;在 官方的MindSearch页面 复制Spaces应用到自己的Spaces下&#xff0c;Space 名称中需要包含 MindSearch 关键词&#xff0c;请在必要的步骤以及成功的对话测试结果当中 1.在github codespace中配置环境 conda create -n mindsearch python3.10 -y conda…

【PyQt5教程 二】Qt Designer 信号与槽的使用方法及PyQt5基本小部件说明

目录 一、信号与槽机制&#xff1a; 二、信号与槽使用方法&#xff1a; &#xff08;1&#xff09;使用Qt Designer 的信号与槽编辑器&#xff1a; &#xff08;2&#xff09;使用固定语法直接建立信号槽连接&#xff1a; 三、PyQt小部件及其触发信号&#xff1a; &#x…

基于PHP课堂签到系统的设计与实现

摘 要 随着教育业的迅速发展和学生人数的不断增加&#xff0c;导致在班级登记制度中传统的“点到”方式不能适应学校的实际需要。从而需要设计一个好的课堂签到系统将会对课堂签到管理工作带来事半功倍的效果。文章着重介绍了基于实践应用的班级签到系统的开发流程&#xff0c…

CSS学习记录11

CSS布局 - display属性 display属性是用于控制布局的最终要的CSS属性。display 属性规定是否/如何显示元素。每个HTML元素都有一个默认的display值&#xff0c;具体取决于它的元素类型。大多数元素的默认display值为block 或 inline。 块级元素&#xff08;block element&…

高效利用资源:分布式有状态服务的高可靠性设计

在分布式系统设计中&#xff0c;实现有状态服务的高可靠性通常采用主备切换的方式。当主服务停止工作时&#xff0c;备服务接管任务&#xff0c;例如通过Keepalive实现VIP的切换以保证可用性。然而&#xff0c;这种方式存在资源浪费的问题&#xff0c;因为备服务始终处于空转状…

重生之我在异世界学智力题(2)

大家好&#xff0c;这里是小编的博客频道 小编的博客&#xff1a;就爱学编程 很高兴在CSDN这个大家庭与大家相识&#xff0c;希望能在这里与大家共同进步&#xff0c;共同收获更好的自己&#xff01;&#xff01;&#xff01; 本文目录 引言智力题&#xff1a;逃离孤岛智力题&a…

论文浅尝 | SAC-KG:利用大语言模型作为领域知识图谱熟练的自动化构造器(ACL2024)...

笔记整理&#xff1a;杜超超&#xff0c;天津大学硕士&#xff0c;研究方向为自然语言处理、大语言模型 论文链接&#xff1a;https://aclanthology.org/2024.acl-long.238/ 发表会议&#xff1a;ACL 2024 1. 动机 知识图谱&#xff08;KG&#xff09;在各个专业领域的知识密集…

Python机器视觉的学习

一、二值化 1.1 二值化图 二值化图&#xff1a;就是将图像中的像素改成只有两种值&#xff0c;其操作的图像必须是灰度图。 1.2 阈值法 阈值法&#xff08;Thresholding&#xff09;是一种图像分割技术&#xff0c;旨在根据像素的灰度值或颜色值将图像分成不同的区域。该方法…

Elasticsearch高性能实践

前言 本方案主要从运维层面分析es是实际生产使用过程中的参数优化&#xff0c;深入理解es各个名词及含义&#xff0c;深入分析es的使用过程中应注意的点&#xff0c;详细解释参数设置的原因以及目的&#xff0c;主要包括系统层面&#xff0c;参数层面。除此之外&#xff0c;优…

笔记本外接显示屏没声音

1、笔记本正常有声音&#xff0c;但是外接显示屏后没有声音了怎么回事呢&#xff1f;原来外接显示屏后笔记本的声音输出会自动选择显示屏的音频输出&#xff0c;但是显示屏可能没有声音输出所以导致笔记本没有声音。 2、解决办法&#xff1a;打开笔记本设置&#xff0c;选择声…

JavaWeb学习(4)(四大域、HttpSession原理(面试)、SessionAPI、Session实现验证码功能)

目录 一、web四大域。 &#xff08;1&#xff09;基本介绍。 &#xff08;2&#xff09;RequestScope。(请求域) &#xff08;3&#xff09;SessionScope。(会话域) &#xff08;4&#xff09;ApplicationScope。(应用域) &#xff08;5&#xff09;PageScope。(页面域) 二、Ht…

用人话讲计算机:Python篇!(十二)正则运算+re模块

目录 一、正则表达式 &#xff08;1&#xff09;什么是正则表达式 &#xff08;2&#xff09;它的结构及使用 示例&#xff1a; 1.字符 . &#xff08;←这里有个小点哦&#xff09; 2.字符 | 3.字符 [ ] 4.字符^ 5.字符\d &#xff08;3&#xff09;补充&#xff…

力扣打卡12:复原IP地址

链接&#xff1a;93. 复原 IP 地址 - 力扣&#xff08;LeetCode&#xff09; 这道题需要对字符串进行操作&#xff0c;我选择了三层循环&#xff0c;其实还可以递归。 我在循环时进行了剪枝&#xff0c;比如一些情况直接跳出循环。 我的代码&#xff1a; class Solution { p…

说下JVM中一次完整的GC流程?

大家好&#xff0c;我是锋哥。今天分享关于【说下JVM中一次完整的GC流程&#xff1f;】面试题。希望对大家有帮助&#xff1b; 说下JVM中一次完整的GC流程&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在JVM中&#xff0c;垃圾回收&#xff08;GC&am…

vs配置c++标准

在 vcxproj 文件中添加 <LanguageStandard>stdcpp17</LanguageStandard> 和在 Visual Studio 属性页面中设置 “C语言标准” 为 “ISO C17 标准 (/std:c17)” 是完全等价的。 它们的对应关系是&#xff1a; VS属性页面中的设置&#xff1a; 项目 -> 属性 ->…