预览 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项目中实现类似的功能,提升应用的用户体验。希望本文对大家有所帮助。

相关推荐
Live0000014 分钟前
在鸿蒙中使用 Repeat 渲染嵌套列表,修改内层列表的一个元素,页面不会更新
前端·javascript·react native
柳杉15 分钟前
使用Ai从零开发智慧水利态势感知大屏(开源)
前端·javascript·数据可视化
兆子龙25 分钟前
从高阶函数到 Hooks:React 如何减轻开发者的心智负担(含 Demo + ahooks 推荐)
前端
狗胜30 分钟前
测试文章 - API抓取
前端
三小河30 分钟前
VS Code 集成 claude-code 教程:告别海外限制,无缝对接国内大模型
前端·程序员
jerrywus35 分钟前
前端老哥的救命稻草:用 Obsidian 搞定 Claude Code 的「金鱼记忆」
前端·agent·claude
球球pick小樱花40 分钟前
游戏官网前端工具库:海内外案例解析
前端·javascript·css
用户60572374873081 小时前
AI 编码助手的规范驱动开发 - OpenSpec 初探
前端·后端·程序员
狗胜1 小时前
AI观察日记 2026-03-02|CLAUDE、TYPE、APPFUNCTIONS:掘金热门里的下一步信号
前端