引言
在现代Web应用中,文件预览功能是非常常见的需求之一。特别是在企业级应用中,用户经常需要查看各种类型的文件,如 PDF、Word、Excel 等。本文将详细介绍如何在Vue项目中实现 PDF 文档的预览功能。
实现原理
后端API
后端需要提供一个API接口,用于获取文件的二进制数据。这个接口通常会根据文件名或文件ID来返回文件内容。
前端处理
前端通过调用后端 API 获取文件的二进制数据,并将其转换为 Blob
对象。然后使用window.URL.createObjectURL
方法生成一个临时的 URL,最后通过 window.open
方法在新窗口中打开这个 URL,从而实现文件预览。
代码示例
node 服务端代码
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
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",
});
}
页面代码
<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>
实现效果
设置网页标题为文件名
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项目中实现类似的功能,提升应用的用户体验。希望本文对大家有所帮助。