在线预览 Word 文档

引言

随着互联网技术的发展,Web 应用越来越复杂,用户对在线办公的需求也日益增加。在许多业务场景中,能够直接在浏览器中预览 Word 文档是一个非常实用的功能。这不仅可以提高用户体验,还能减少用户操作步骤,提升效率。

实现原理

1. 后端服务

假设后端服务已经提供了两个 API 接口:

getFilesList: 获取文件列表。
previewFile: 获取指定文件的内容。

const express = require('express');
const multer = require('multer');
const fs = require('fs');
const path = require('path');const app = express();// 定义文件夹路径
const mergedDir = path.join(__dirname, 'merged');// 获取文件列表
app.get('/getFilesList', (req, res) => {fs.readdir(mergedDir, (err, files) => {if (err) {return res.status(500).json({ error: '无法读取文件夹' });}// 获取每个文件的详细信息const fileInfos = files.map(file => {const filePath = path.join(mergedDir, file);const stats = fs.statSync(filePath);return {fileName: file,size: stats.size,upTime: stats.mtime,isFile: stats.isFile()};});let resContent = {code: 200,data: fileInfos || [],message: '查询成功'}res.json(resContent);});
});// 文件预览接口
app.get('/download', (req, res) => {const { fileName } = req.query;const filePath = path.join(mergedDir, fileName);fs.access(filePath, fs.constants.F_OK, (err) => {if (err) {return res.status(404).json({ error: '文件不存在' });}const stats = fs.statSync(filePath);if (stats.isFile()) {const contentType = getContentType(fileName);res.setHeader('Content-Type', contentType);// 对 fileName 进行编码const encodedFileName = encodeURIComponent(fileName);res.setHeader('Content-Disposition', `inline; filename=${encodedFileName}`);fs.createReadStream(filePath).pipe(res);} else {res.status(400).json({ error: '不是一个文件' });}});
});// 获取文件的 MIME 类型
function getContentType(fileName) {const ext = path.extname(fileName).toLowerCase();switch (ext) {case '.js':return 'application/javascript';case '.json':return 'application/json';case '.html':return 'text/html';case '.css':return 'text/css';case '.txt':return 'text/plain';case '.png':return 'image/png';case '.jpg':case '.jpeg':return 'image/jpeg';case '.gif':return 'image/gif';case '.pdf':return 'application/pdf';case '.doc':return 'application/msword';case '.docx':return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';case '.ppt':return 'application/vnd.ms-powerpoint';case '.pptx':return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';case '.xlsx':return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';default:return 'application/octet-stream';}
}// 启动服务器
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {console.log(`Server is running on port ${PORT}`);
});

2. 前端页面

前端页面使用 Vue.js 和 Element UI 组件库来实现文件列表展示和预览功能。

文件列表展示

  • 在组件的 created 生命周期钩子中调用 getFileName 方法,从后端获取文件列表。

  • 使用 timeStampToString 工具函数将时间戳转换为可读的日期格式。

  • 将处理后的数据赋值给 tableData,并在表格中展示。

async getFileName() {const { code, data: resData } = await getFilesList(this.form);if (code === 200) {resData.forEach(item => {item.upTime = timeStampToString(item.upTime);});this.tableData = resData;}
}

预览 Word 文档

  • 当用户点击“预览”按钮时,触发 previewDocx 方法。

  • 该方法接收当前行的数据对象 { fileName },并调用 previewFile API 获取文件内容。

async previewDocx({ fileName }) {try {const response = await previewFile(fileName);const reader = new FileReader();reader.onload = async (event) => {const arrayBuffer = event.target.result;const result = await mammoth.convertToHtml({ arrayBuffer });const htmlContent = result.value;// 创建一个新的窗口const newWindow = window.open('', '_blank');// 在新窗口中写入 HTML 内容newWindow.document.write(`<!DOCTYPE html><html><head><title>Word 文档预览</title><meta charset="UTF-8"><style>/* 添加一些样式以改善预览效果 */body {font-family: Arial, sans-serif;margin: 20px;}</style></head><body>${htmlContent}</body></html>`);// 确保新窗口的内容加载完成newWindow.document.close();};reader.readAsArrayBuffer(response);} catch (err) {console.error('Failed to preview file', err);}
}

使用 Mammoth 库文件转换

  • mammoth 是一个将 Word 文档(.docx)转换为 HTML 的库。
  • 在 reader.onload 回调中,使用 mammoth.convertToHtml 方法将文件的二进制数据(ArrayBuffer)转换为 HTML 字符串。
const result = await mammoth.convertToHtml({ arrayBuffer });
const htmlContent = result.value;

创建新窗口预览 word 文档

  • 使用 window.open 方法创建一个新的浏览器窗口。

  • 在新窗口中写入转换后的 HTML 内容,并添加一些基本的样式以改善预览效果。

const newWindow = window.open('', '_blank');
newWindow.document.write(`<!DOCTYPE html><html><head><title>Word 文档预览</title><meta charset="UTF-8"><style>body {font-family: Arial, sans-serif;margin: 20px;}</style></head><body>${htmlContent}</body></html>
`);
newWindow.document.close();

缺点:使用 mammoth 将 Word 文档转换为 HTML 时,会丢失 Word 文档中的样式,导致渲染的页面与原 word 文档差异很大。

转换后的 HTML 与原 Word 文档对比如下
在这里插入图片描述

解决办法: 使用 docx-preview 预览 Word 文档

使用 docx-preview 预览 Word 文档

介绍

docx-preview 是一个用于在浏览器中预览 Word 文档(.docx)的 JavaScript 库。它提供了简单易用的 API 来渲染 Word 文档。

安装

npm install docx-preview
# 或者
yarn add docx-preview

主要方法

1. renderAsync

参数:

  • document:Word 文档的二进制数据,可以是 BlobArrayBufferstring 类型。

  • bodyContainer:DOM 元素,用于放置渲染后的文档内容。

  • styleContainer: DOM 元素,用于渲染文档的样式、编号、字体等。如果设置为 null,则使用 bodyContainer

  • options:一个对象,包含以下可选属性:

    • className: string (默认值: "docx"),用于生成默认和文档样式的类名前缀。

    • inWrapper: boolean (默认值: true),是否启用围绕文档内容的包装器,如果启用,文档内容将被包裹在一个额外的容器中。

    • ignoreWidth: boolean (默认值: false),是否禁用页面宽度的渲染,如果启用,页面宽度将不会被应用到渲染的 HTML 中。

    • ignoreHeight: boolean (默认值: false),是否禁用页面高度的渲染,如果启用,页面高度将不会被应用到渲染的 HTML 中。

    • ignoreFonts: boolean (默认值: false),是否禁用字体的渲染,如果启用,文档中的自定义字体将不会被加载和应用。

    • breakPages: boolean (默认值: true),是否在页面中断处进行分页,如果启用,页面中断(如分页符)将被正确处理。

    • ignoreLastRenderedPageBreak: boolean (默认值: true),是否禁用最后一个渲染的页面中断,如果启用,最后一个页面中断将不会被处理。

    • experimental: boolean (默认值: false),是否启用实验性功能(如制表位计算),启用后,可以使用一些尚未完全稳定的高级功能。

    • trimXmlDeclaration: boolean (默认值: true),是否在解析 XML 文档之前移除 XML 声明,如果启用,XML 声明将被移除,以避免解析问题。

    • useBase64URL: boolean (默认值: false),是否将图像、字体等资源转换为 Base64 URL,如果启用,资源将被嵌入到 HTML 中,而不是使用 URL.createObjectURL。

    • renderChanges: boolean (默认值: false),是否启用实验性的文档更改渲染(如插入和删除),启用后,文档中的更改标记将被渲染。

    • renderHeaders: boolean (默认值: true),是否启用页眉的渲染,如果启用,文档中的页眉将被正确渲染。

    • renderFooters: boolean (默认值: true),是否启用页脚的渲染,如果启用,文档中的页脚将被正确渲染。

    • renderFootnotes: boolean (默认值: true),是否启用脚注的渲染,如果启用,文档中的脚注将被正确渲染。

    • renderEndnotes: boolean (默认值: true),是否启用尾注的渲染,如果启用,文档中的尾注将被正确渲染。

    • renderComments: boolean (默认值: false),是否启用实验性的评论渲染,启用后,文档中的评论将被渲染。

    • debug: boolean (默认值: false),是否启用额外的调试日志,启用后,将在控制台输出更多的调试信息,有助于问题排查。

示例

import { renderAsync } from 'your-render-library';const documentBlob = /* 获取 Word 文档的 Blob 数据 */;
const bodyContainer = document.getElementById('body-container');
const styleContainer = document.getElementById('style-container');const options = {className: 'docx',inWrapper: true,ignoreWidth: false,ignoreHeight: false,ignoreFonts: false,breakPages: true,ignoreLastRenderedPageBreak: true,experimental: false,trimXmlDeclaration: true,useBase64URL: false,renderChanges: false,renderHeaders: true,renderFooters: true,renderFootnotes: true,renderEndnotes: true,renderComments: false,debug: false
};renderAsync(documentBlob, bodyContainer, styleContainer, options).then((wordDocument) => {console.log('Document rendered successfully:', wordDocument);}).catch((error) => {console.error('Error rendering document:', error);});

预览 Word 文档示例代码

<template><div class="table"><el-table :data="tableData" header-align="center" border style="width: 100%"><el-table-column align="center" type="index" width="60" label="序号"></el-table-column><el-table-column align="center" prop="fileName" label="文件名" /><el-table-column align="center" prop="upTime" label="上传日期" width="200" /><el-table-column align="center" fixed="right" label="操作" width="120"><template slot-scope="scope"><el-button @click="previewDocx(scope.row)" type="text">预览</el-button></template></el-table-column></el-table><el-dialog width="68%" :visible.sync="isShow" :before-close="close" class="doxc_dialog" :show-close="false":close-on-press-escape="true"><div class="doxc_con" ref="doxc_con"></div></el-dialog></div>
</template><script>
import { previewFile } from "@/api/file";
import { timeStampToString } from "@/utils/utils";
import { getFilesList } from "@/api/file";
import { renderAsync } from 'docx-preview';
export default {name: "fileTable",data() {return {pageSizes: [10, 20, 50, 100],form: {pageSize: 10,pageNum: 1,},total: 0,tableData: [],isShow: false,}},created() {this.getFileName();},methods: {// 获取文件列表async getFileName() {const { code, data: resData } = await getFilesList(this.form);console.log('code, data::: ', code, resData);if (code === 200) {resData.forEach(item => {item.upTime = timeStampToString(item.upTime);});this.tableData = resData;}},// 预览 DOCX 文件async previewDocx({ fileName }) {try {const response = await previewFile(fileName);console.log('response::: ', response);let blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });this.isShow = true;// 使用 dialog 组件,需要弹窗完成后再渲染内容this.$nextTick(async () => {// 渲染预览await renderAsync(response,this.$refs.doxc_con,);});} catch (err) {console.error('Failed to preview file', err);}},close() {this.isShow = false;},}
}
</script><style lang="scss" scoped>
.table {width: 600px;
}.pagination {float: right;margin-top: 20px;
}::v-deep .doxc_dialog .el-dialog__body {padding: 0 !important;
}::v-deep .doxc_dialog .el-dialog {margin-top: 5vh !important;width: 595.3pt !important;
}::v-deep .doxc_dialog .el-dialog__header {display: none;
}::v-deep .doxc_dialog .docx-wrapper {padding: 0 !important;
}
</style>

实现效果

在这里插入图片描述

总结

本文介绍了如何在 Web 应用中实现 Word 文档的预览功能。后端使用 Express 框架提供文件列表和文件内容的 API 接口,前端使用 Vue.js 和 Element UI 组件库展示文件列表并实现预览功能。通过 mammothdocx-preview 库将 Word 文档转换为 HTML 并在新窗口或对话框中展示,确保了良好的用户体验和较高的预览质量。

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

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

相关文章

C++ 优先算法 —— 查找总价格为目标值的两个商品(双指针)

目录 题目 &#xff1a;查找总价格为目标值的两个商品 1. 题目解析 2. 算法原理 Ⅰ 暴力枚举 Ⅱ 双指针算法 3. 代码实现 暴力枚举 双指针算法 题目 &#xff1a;查找总价格为目标值的两个商品 1. 题目解析 题目截图&#xff1a; 这道题的一个关键的地方&#xff0c;它先…

Qt QCheckBox、QPushButton和QRadioButton详解

QCheckBox&#xff08;复选框&#xff09; 功能&#xff1a;QCheckBox用于创建一个复选框控件&#xff0c;允许用户从多个选项中选择多个。 属性&#xff1a; checkable&#xff1a;决定复选框是否可以被选中或取消选中。checked&#xff1a;表示复选框当前的选中状态&#…

自编以e为底的指数函数exp,性能接近标准库函数

算法描述&#xff1a; (1). 先做自变量x的范围检查&#xff0c;对于双精度浮点数&#xff0c;自变量不能超出(-1022ln2, 1024ln2)(-708.39, 709.78)&#xff0c;否则exp(x)会溢出。对于单精度浮点数&#xff0c;自变量不能超出(-126ln2, 128ln2)(-87.33, 88.72). 自己使用此函数…

数据结构-二叉树中的递归

目录 前言 简单手撕二叉树 二叉树节点的求解 二叉树叶子节点的求解 二叉树高度 二叉树第K层节点的个数 二叉树查找值为X的节点 结束语 前言 在这里说声抱歉&#xff0c;好久没更新数据结构了&#xff0c;二叉树的相关内容还没有更新完&#xff0c;是小编的失职&#xff…

在基于AWS EC2的云端k8s环境中 搭建开发基础设施

中间件下载使用helm,这里部署的都是单机版的 aws-ebs-storageclass.yaml apiVersion: storage.k8s.io/v1 kind: StorageClass metadata:name: aws-ebs-storageclass provisioner: kubernetes.io/aws-ebs parameters:type: gp2 # 选择合适的 EBS 类型&#xff0c;如 gp2、io1…

2024网鼎杯青龙组wp:Crypto1

题目 附件内容如下 from Crypto.Util.number import * from secret import flag from Cryptodome.PublicKey import RSAp getPrime(512) q getPrime(512) n p * q d getPrime(299) e inverse(d,(p-1)*(q-1)) m bytes_to_long(flag) c pow(m,e,n) hint1 p >> (51…

Golang | Leetcode Golang题解之第528题按权重随机选择

题目&#xff1a; 题解&#xff1a; type Solution struct {pre []int }func Constructor(w []int) Solution {for i : 1; i < len(w); i {w[i] w[i-1]}return Solution{w} }func (s *Solution) PickIndex() int {x : rand.Intn(s.pre[len(s.pre)-1]) 1return sort.Searc…

3D打印机 屏幕的固定挂钩断后的一次自己修复经历

引子 3D打印机的屏幕固定挂钩断了 这次确实不知道咋断的&#xff0c;这可咋办呢&#xff0c;到网上看了一下&#xff0c;一个屏幕要2佰多&#xff0c;有些小贵&#xff0c;要不就自己修修吧&#xff0c;打个挂钩按上&#xff0c;说干就干。 正文 freecad的设计图如下【其中各…

PHP合成图片,生成海报图,poster-editor使用说明

之前写过一篇使用Grafika插件生成海报图的文章&#xff0c;但是当我再次使用时&#xff0c;却发生了错误&#xff0c;回看Grafika文档&#xff0c;发现很久没更新了&#xff0c;不兼容新版的GD&#xff0c;所以改用了intervention/image插件来生成海报图。 但是后来需要对海报…

Java基于微信小程序的美食推荐系统(附源码,文档)

博主介绍&#xff1a;✌程序猿徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

Linux的IP网路命令: 用于显示和操作网络接口(网络设备)的命令ip link详解

目录 一、概述 二、用法 1、基本语法 2、常用选项 3、常用参数 4、获取帮助 三、示例 1. 显示所有网络接口的信息 &#xff08;1&#xff09;命令 &#xff08;2&#xff09;输出示例 &#xff08;3&#xff09;实际操作 2. 启动网络接口 3. 停止网络接口 4. 更改…

C语言 | Leetcode C语言题解之第526题优美的排列

题目&#xff1a; 题解&#xff1a; int countArrangement(int n) {int f[1 << n];memset(f, 0, sizeof(f));f[0] 1;for (int mask 1; mask < (1 << n); mask) {int num __builtin_popcount(mask);for (int i 0; i < n; i) {if (mask & (1 <<…

SpringBoot篇(自动装配原理)

目录 一、自动装配机制 1. 简介 2. 自动装配主要依靠三个核心的关键技术 3. run()方法加载启动类 4. 注解SpringBootApplication包含了多个注解 4.1 SpringBootConfiguration 4.2 ComponentScan 4.3 EnableAutoConfiguration 5. SpringBootApplication一共做了三件事 …

3D Gaussian Splatting代码详解(二):模型构建

3 模型构建 gaussians GaussianModel(dataset.sh_degree) 3.1 初始化函数 __init__ 构造函数 构造函数 __init__ 的主要作用是初始化 3D 高斯模型的各项参数和激活函数&#xff0c;用于生成 3D 空间中的高斯表示。 初始化球谐函数的参数&#xff1a; self.active_sh_degre…

如何在 linux 中使用 /etc/fstab 挂载远程共享 ?

在 Linux 领域&#xff0c;高效的管理文件系统和数据存储对于用户和管理员来说&#xff0c;是一项基本技能。 有一种特别有用的技术涉及自动建立远程共享&#xff0c;允许无缝访问网络存储&#xff0c;就好像是本地的一样。 本指南将引导您完成使用 /etc/fstab 文件以自动远程…

iOS用rime且导入自制输入方案

iPhone 16 的 cantonese 只能打传统汉字&#xff0c;没有繁简转换&#xff0c;m d sh d。考虑用「仓」输入法 [1] 使用 Rime 打字&#xff0c;且希望导入自制方案 [2]。 仓输入法有几种导入方案的方法&#xff0c;见 [3]&#xff0c;此处记录 wifi 上传法。准备工作&#xff1…

ts:常见的运算符

ts&#xff1a;常见的运算符 1 主要内容说明2 表格2.1 算数运算符2.2 赋值运算符2.3 比较运算符2.4 逻辑运算符2.5 位运算符2.6 三元运算符 3 例子3.1 位运算符3.1.1 源码1 &#xff08;位运算符&#xff09;3.1.2 源码1运行效果 3.结语4.定位日期 1 主要内容说明 ts中的各种运…

unity搭建场景学习

unity搭建场景学习 创建场景创建gameobject创建材质&#xff0c;用于给gameobject上色拖拽材质球上色上色原理设置多个材质方式设置贴图的方式 效果设置光滑度一些预览设置菜单渲染模型与碰撞模型网格渲染参数1. materials(材质)2. lighting(光照)3. reflection probes(反射探针…

『Linux学习笔记』如何在 Ubuntu 22.04 上安装和配置 VNC

『Linux学习笔记』如何在 Ubuntu 22.04 上安装和配置 VNC 文章目录 一. 『Linux学习笔记』如何在 Ubuntu 22.04 上安装和配置 VNC1. 介绍 二. 参考文献 一. 『Linux学习笔记』如何在 Ubuntu 22.04 上安装和配置 VNC 如何在 Ubuntu 22.04 上安装和配置 VNChttps://hub.docker.c…

xlwings,让excel飞起来!

excel已经成为必不可少的数据处理软件&#xff0c;几乎天天在用。python有很多支持操作excel的第三方库&#xff0c;xlwings是其中一个。 关于xlwings xlwings开源免费&#xff0c;能够非常方便的读写Excel文件中的数据&#xff0c;并且能够进行单元格格式的修改。 xlwings还…