预览 PDF 文档

引言

在现代Web应用中,文件预览功能是非常常见的需求之一。特别是在企业级应用中,用户经常需要查看各种类型的文件,如 PDF、Word、Excel 等。本文将详细介绍如何在Vue项目中实现 PDF 文档的预览功能。

实现原理

后端API

后端需要提供一个API接口,用于获取文件的二进制数据。这个接口通常会根据文件名或文件ID来返回文件内容。

前端处理

前端通过调用后端 API 获取文件的二进制数据,并将其转换为 Blob 对象。然后使用window.URL.createObjectURL 方法生成一个临时的 URL,最后通过 window.open 方法在新窗口中打开这个 URL,从而实现文件预览。

代码示例

node 服务端代码

javascript 复制代码
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('/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', `attachment; 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}`);
});

前端代码实现

接口 API

javascript 复制代码
import request from "@/utils/request";

// 获取文件列表
export function getFilesList() {
  return request({
    url: "/getFilesList",
    method: "get",
  });
}

// 文件预览
export function previewFile(fileName) {
  return request({
    url: `/download?fileName=${fileName}`,
    method: "get",
    responseType: "blob",
  });
}

页面代码

javascript 复制代码
<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="handleClick(scope.row)" type="text">预览</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
import { previewFile } from "@/api/file";
import { timeStampToString } from "@/utils/utils";
import { getFilesList } from "@/api/file";

export default {
  name: "fileTable",
  data() {
    return {
      tableData: [],
    }
  },
  created() {
    this.getFileName();
  },
  methods: {
    // 获取文件列表
    async getFileName() {
      const { code, data: resData } = await getFilesList();
      console.log('code, data::: ', code, resData);
      if (code === 200) {
        resData.forEach(item => {
          item.upTime = timeStampToString(item.upTime);
        });
        this.tableData = resData;
      }
    },
    handleBlob(blob, filename) {
      console.log('blob::: ', blob,filename);
      let url = window.URL.createObjectURL(blob);
      var tempwindow = window.open("_blank");
      if (tempwindow) {
        tempwindow.location = url;
      }
    },
    // 文件预览
    async handleClick(row) {
      try {
        const response = await previewFile(row.fileName)
        console.log('response::: ', response);
        this.handleBlob(response, row.fileName)
      } catch (err) {
        console.error('Failed to preview file', err);
      }
    },
  }
}
</script>

实现效果

设置网页标题为文件名

javascript 复制代码
 handleBlob(blob, filename) {
      console.log('blob::: ', blob, filename);
      let url = window.URL.createObjectURL(blob);

      // 创建一个新窗口
      let tempWindow = window.open("", "_blank");

      // 设置新窗口的标题为文件名
      tempWindow.document.title = filename;

      // 在新窗口中嵌入一个 <iframe> 来预览 PDF 文件
      tempWindow.document.write('<html><head><title>' + filename + '</title></head><body>');
      tempWindow.document.write('<iframe src="' + url + '" style="width:100%;height:100%;border:none;"></iframe>');
      tempWindow.document.write('</body></html>');

      // 确保新窗口的内容加载完成后再释放 URL
      tempWindow.onload = function () {
        window.URL.revokeObjectURL(url);
      };
    },

修改后的效果

总结

本文详细介绍了如何在Vue项目中实现PDF文档的预览功能。通过前后端的协同工作,我们实现了从文件的存储、获取到预览的完整流程。具体来说:

  • 后端API:提供了文件下载接口,根据文件名或文件ID返回文件的二进制数据,并设置了正确的MIME类型。

  • 前端处理:通过调用后端API获取文件的二进制数据,将其转换为 Blob 对象,并生成一个临时的 URL。然后在新窗口中打开这个 URL,实现文件预览。

  • 优化体验:为了提升用户体验,我们进一步优化了预览功能,通过在新窗口中嵌入 <iframe> 并设置网页标题为文件名,使得预览界面更加友好和直观。

通过本文的介绍,读者可以轻松地在自己的Vue项目中实现类似的功能,提升应用的用户体验。希望本文对大家有所帮助。

相关推荐
小丁爱养花2 分钟前
前端三剑客(三):JavaScript
开发语言·前端·javascript
ZwaterZ10 分钟前
vue el-table表格点击某行触发事件&&操作栏点击和row-click冲突问题
前端·vue.js·elementui·c#·vue
码农六六10 分钟前
vue3封装Element Plus table表格组件
javascript·vue.js·elementui
西凉河的葛三叔14 分钟前
vue3+elementui-plus el-dialog全局配置点击空白处不关闭弹窗
前端·vue3·elementui-plus
徐同保15 分钟前
el-table 多选改成单选
javascript·vue.js·elementui
快乐小土豆~~15 分钟前
el-input绑定点击回车事件意外触发页面刷新
javascript·vue.js·elementui
生信摆渡16 分钟前
R语言-快速对多个变量取交集
开发语言·数据库·r语言
周三有雨22 分钟前
【面试题系列Vue07】Vuex是什么?使用Vuex的好处有哪些?
前端·vue.js·面试·typescript
¥ 多多¥29 分钟前
c++中mystring运算符重载
开发语言·c++·算法
木古古1834 分钟前
使用chrome 访问虚拟机Apache2 的默认页面,出现了ERR_ADDRESS_UNREACHABLE这个鸟问题
前端·chrome·apache