在线预览 Word 文档

打印 上一主题 下一主题

主题 1956|帖子 1956|积分 5868

弁言

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

1. 后端服务

假设后端服务已经提供了两个 API 接口:
getFilesList: 获取文件列表。
previewFile: 获取指定文件的内容。
  1. const express = require('express');
  2. const multer = require('multer');
  3. const fs = require('fs');
  4. const path = require('path');
  5. const app = express();
  6. // 定义文件夹路径
  7. const mergedDir = path.join(__dirname, 'merged');
  8. // 获取文件列表
  9. app.get('/getFilesList', (req, res) => {
  10.     fs.readdir(mergedDir, (err, files) => {
  11.         if (err) {
  12.             return res.status(500).json({ error: '无法读取文件夹' });
  13.         }
  14.         // 获取每个文件的详细信息
  15.         const fileInfos = files.map(file => {
  16.             const filePath = path.join(mergedDir, file);
  17.             const stats = fs.statSync(filePath);
  18.             return {
  19.                 fileName: file,
  20.                 size: stats.size,
  21.                 upTime: stats.mtime,
  22.                 isFile: stats.isFile()
  23.             };
  24.         });
  25.         let resContent = {
  26.             code: 200,
  27.             data: fileInfos || [],
  28.             message: '查询成功'
  29.         }
  30.         res.json(resContent);
  31.     });
  32. });
  33. // 文件预览接口
  34. app.get('/download', (req, res) => {
  35.     const { fileName } = req.query;
  36.     const filePath = path.join(mergedDir, fileName);
  37.     fs.access(filePath, fs.constants.F_OK, (err) => {
  38.         if (err) {
  39.             return res.status(404).json({ error: '文件不存在' });
  40.         }
  41.         const stats = fs.statSync(filePath);
  42.         if (stats.isFile()) {
  43.             const contentType = getContentType(fileName);
  44.             res.setHeader('Content-Type', contentType);
  45.             // 对 fileName 进行编码
  46.             const encodedFileName = encodeURIComponent(fileName);
  47.             res.setHeader('Content-Disposition', `inline; filename=${encodedFileName}`);
  48.             fs.createReadStream(filePath).pipe(res);
  49.         } else {
  50.             res.status(400).json({ error: '不是一个文件' });
  51.         }
  52.     });
  53. });
  54. // 获取文件的 MIME 类型
  55. function getContentType(fileName) {
  56.     const ext = path.extname(fileName).toLowerCase();
  57.     switch (ext) {
  58.         case '.js':
  59.             return 'application/javascript';
  60.         case '.json':
  61.             return 'application/json';
  62.         case '.html':
  63.             return 'text/html';
  64.         case '.css':
  65.             return 'text/css';
  66.         case '.txt':
  67.             return 'text/plain';
  68.         case '.png':
  69.             return 'image/png';
  70.         case '.jpg':
  71.         case '.jpeg':
  72.             return 'image/jpeg';
  73.         case '.gif':
  74.             return 'image/gif';
  75.         case '.pdf':
  76.             return 'application/pdf';
  77.         case '.doc':
  78.             return 'application/msword';
  79.         case '.docx':
  80.             return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document';
  81.         case '.ppt':
  82.             return 'application/vnd.ms-powerpoint';
  83.         case '.pptx':
  84.             return 'application/vnd.openxmlformats-officedocument.presentationml.presentation';
  85.         case '.xlsx':
  86.             return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
  87.         default:
  88.             return 'application/octet-stream';
  89.     }
  90. }
  91. // 启动服务器
  92. const PORT = process.env.PORT || 3000;
  93. app.listen(PORT, () => {
  94.     console.log(`Server is running on port ${PORT}`);
  95. });
复制代码
2. 前端页面

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


  • 在组件的 created 生命周期钩子中调用 getFileName 方法,从后端获取文件列表。
  • 使用 timeStampToString 工具函数将时间戳转换为可读的日期格式。
  • 将处理后的数据赋值给 tableData,并在表格中展示。
  1. async getFileName() {
  2.   const { code, data: resData } = await getFilesList(this.form);
  3.   if (code === 200) {
  4.     resData.forEach(item => {
  5.       item.upTime = timeStampToString(item.upTime);
  6.     });
  7.     this.tableData = resData;
  8.   }
  9. }
复制代码
预览 Word 文档


  • 当用户点击“预览”按钮时,触发 previewDocx 方法。
  • 该方法吸收当前行的数据对象 { fileName },并调用 previewFile API 获取文件内容。
  1. async previewDocx({ fileName }) {
  2.   try {
  3.     const response = await previewFile(fileName);
  4.     const reader = new FileReader();
  5.     reader.onload = async (event) => {
  6.       const arrayBuffer = event.target.result;
  7.       const result = await mammoth.convertToHtml({ arrayBuffer });
  8.       const htmlContent = result.value;
  9.       // 创建一个新的窗口
  10.       const newWindow = window.open('', '_blank');
  11.       // 在新窗口中写入 HTML 内容
  12.       newWindow.document.write(`
  13.         <!DOCTYPE html>
  14.         <html>
  15.         <head>
  16.           <title>Word 文档预览</title>
  17.           <meta charset="UTF-8">
  18.           <style>
  19.             /* 添加一些样式以改善预览效果 */
  20.             body {
  21.               font-family: Arial, sans-serif;
  22.               margin: 20px;
  23.             }
  24.           </style>
  25.         </head>
  26.         <body>
  27.           ${htmlContent}
  28.         </body>
  29.         </html>
  30.       `);
  31.       // 确保新窗口的内容加载完成
  32.       newWindow.document.close();
  33.     };
  34.     reader.readAsArrayBuffer(response);
  35.   } catch (err) {
  36.     console.error('Failed to preview file', err);
  37.   }
  38. }
复制代码
使用 Mammoth 库文件转换


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


  • 使用 window.open 方法创建一个新的浏览器窗口。
  • 在新窗口中写入转换后的 HTML 内容,并添加一些基本的样式以改善预览结果。
  1. const newWindow = window.open('', '_blank');
  2. newWindow.document.write(`
  3.   <!DOCTYPE html>
  4.   <html>
  5.   <head>
  6.     <title>Word 文档预览</title>
  7.     <meta charset="UTF-8">
  8.     <style>
  9.       body {
  10.         font-family: Arial, sans-serif;
  11.         margin: 20px;
  12.       }
  13.     </style>
  14.   </head>
  15.   <body>
  16.     ${htmlContent}
  17.   </body>
  18.   </html>
  19. `);
  20. newWindow.document.close();
复制代码
  缺点:使用 mammoth 将 Word 文档转换为 HTML 时,会丢失 Word 文档中的样式,导致渲染的页面与原 word 文档差别很大。
  转换后的 HTML 与原 Word 文档对比如下

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

先容

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

  1. npm install docx-preview
  2. # 或者
  3. yarn add docx-preview
复制代码
重要方法

1. renderAsync
参数:


  • document:Word 文档的二进制数据,可以是 Blob、ArrayBuffer 或 string 范例。
  • 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),是否启用额外的调试日志,启用后,将在控制台输出更多的调试信息,有助于问题排查。

示例
  1. import { renderAsync } from 'your-render-library';
  2. const documentBlob = /* 获取 Word 文档的 Blob 数据 */;
  3. const bodyContainer = document.getElementById('body-container');
  4. const styleContainer = document.getElementById('style-container');
  5. const options = {
  6.     className: 'docx',
  7.     inWrapper: true,
  8.     ignoreWidth: false,
  9.     ignoreHeight: false,
  10.     ignoreFonts: false,
  11.     breakPages: true,
  12.     ignoreLastRenderedPageBreak: true,
  13.     experimental: false,
  14.     trimXmlDeclaration: true,
  15.     useBase64URL: false,
  16.     renderChanges: false,
  17.     renderHeaders: true,
  18.     renderFooters: true,
  19.     renderFootnotes: true,
  20.     renderEndnotes: true,
  21.     renderComments: false,
  22.     debug: false
  23. };
  24. renderAsync(documentBlob, bodyContainer, styleContainer, options)
  25.     .then((wordDocument) => {
  26.         console.log('Document rendered successfully:', wordDocument);
  27.     })
  28.     .catch((error) => {
  29.         console.error('Error rendering document:', error);
  30.     });
复制代码
预览 Word 文档示例代码

  1. <template>
  2.   <div class="table">
  3.     <el-table :data="tableData" header-align="center" border style="width: 100%">
  4.       <el-table-column align="center" type="index" width="60" label="序号">
  5.       </el-table-column>
  6.       <el-table-column align="center" prop="fileName" label="文件名" />
  7.       <el-table-column align="center" prop="upTime" label="上传日期" width="200" />
  8.       <el-table-column align="center" fixed="right" label="操作" width="120">
  9.         <template slot-scope="scope">
  10.           <el-button @click="previewDocx(scope.row)" type="text">预览</el-button>
  11.         </template>
  12.       </el-table-column>
  13.     </el-table>
  14.     <el-dialog width="68%" :visible.sync="isShow" :before-close="close" class="doxc_dialog" :show-close="false"
  15.     :close-on-press-escape="true">
  16.       <div class="doxc_con" ref="doxc_con"></div>
  17.     </el-dialog>
  18.   </div>
  19. </template>
  20. <script>
  21. import { previewFile } from "@/api/file";
  22. import { timeStampToString } from "@/utils/utils";
  23. import { getFilesList } from "@/api/file";
  24. import { renderAsync } from 'docx-preview';
  25. export default {
  26.   name: "fileTable",
  27.   data() {
  28.     return {
  29.       pageSizes: [10, 20, 50, 100],
  30.       form: {
  31.         pageSize: 10,
  32.         pageNum: 1,
  33.       },
  34.       total: 0,
  35.       tableData: [],
  36.       isShow: false,
  37.     }
  38.   },
  39.   created() {
  40.     this.getFileName();
  41.   },
  42.   methods: {
  43.     // 获取文件列表
  44.     async getFileName() {
  45.       const { code, data: resData } = await getFilesList(this.form);
  46.       console.log('code, data::: ', code, resData);
  47.       if (code === 200) {
  48.         resData.forEach(item => {
  49.           item.upTime = timeStampToString(item.upTime);
  50.         });
  51.         this.tableData = resData;
  52.       }
  53.     },
  54.     // 预览 DOCX 文件
  55.     async previewDocx({ fileName }) {
  56.       try {
  57.         const response = await previewFile(fileName);
  58.         console.log('response::: ', response);
  59.         let blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' });
  60.         this.isShow = true;
  61.         // 使用 dialog 组件,需要弹窗完成后再渲染内容
  62.         this.$nextTick(async () => {
  63.           // 渲染预览
  64.           await renderAsync(
  65.             response,
  66.             this.$refs.doxc_con,
  67.           );
  68.         });
  69.       } catch (err) {
  70.         console.error('Failed to preview file', err);
  71.       }
  72.     },
  73.     close() {
  74.       this.isShow = false;
  75.     },
  76.   }
  77. }
  78. </script>
  79. <style lang="scss" scoped>
  80. .table {
  81.   width: 600px;
  82. }
  83. .pagination {
  84.   float: right;
  85.   margin-top: 20px;
  86. }
  87. ::v-deep .doxc_dialog .el-dialog__body {
  88.   padding: 0 !important;
  89. }
  90. ::v-deep .doxc_dialog .el-dialog {
  91.   margin-top: 5vh !important;
  92.   width: 595.3pt !important;
  93. }
  94. ::v-deep .doxc_dialog .el-dialog__header {
  95.   display: none;
  96. }
  97. ::v-deep .doxc_dialog .docx-wrapper {
  98.   padding: 0 !important;
  99. }
  100. </style>
复制代码
实现结果

总结

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

笑看天下无敌手

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表