pdf 转文本
主要使用 pdf-parse 这个库,直接识别提取我们 pdf 文件中的文字。
js
const express = require("express");
const fs = require("fs");
const PDFParser = require("pdf-parse");
const cors = require("cors");
const pdfFilePath = "./xxx_实习生.pdf";
const app = express();
// 解决跨域问题
app.use(cors());
app.use(express.static(__dirname));
app.use(express.json());
app.post("/form2pdf", (req, res) => {
console.log(req.body);
const str = "";
// 1. 读取pdf文件
fs.readFile(pdfFilePath, (err, pdfBuffer) => {
if (err) {
console.log(err);
return;
}
PDFParser(pdfBuffer).then((pdfData) => {
const pages = pdfData.text.split("\n\n");
console.log(pages);
let str;
for (let i = 1; i < pages.length; i++) str = str + pages[i];
console.log(str);
});
});
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});

文本转 pdf
主要使用 pdfkit 这个库将文本转为pdf文件。
js
const express = require("express");
const fs = require("fs");
const cors = require("cors");
const PDFDocument = require("pdfkit");
const app = express();
// 解决跨域问题
app.use(cors());
app.use(express.static(__dirname));
app.use(express.json());
app.post("/form2pdf", (req, res) => {
console.log(req.body);
const str = "";
// 使用 pdfkit 生成pdf
// 创建 PDF 文档
const doc = new PDFDocument({
margin: 50, // 控制上下左右留白
});
// 输出到文件
// 如果有同名的 直接覆盖
doc.pipe(fs.createWriteStream("output.pdf"));
// ========== Header ==========
function addHeader(doc) {
doc.fontSize(20).text(req.body.name, { align: "center" }).moveDown();
doc
.moveTo(50, doc.y) // 从左边开始画横线
.lineTo(550, doc.y) // 到右边
.stroke();
doc.moveDown();
}
// ========== Footer ==========
function addFooter(doc) {
const range = doc.bufferedPageRange(); // 所有页
for (let i = range.start; i < range.start + range.count; i++) {
doc.switchToPage(i);
doc
.fontSize(10)
.fillColor("gray")
.text(`第 ${i + 1} 页`, 0, doc.page.height - 50, {
align: "center",
});
}
}
// ========== Body ==========
function addBody(doc) {
doc.fontSize(12);
doc.text(req.body.name + " " + req.body.sex + " " + req.body.age, {
align: "left",
lineGap: 4,
});
}
// 添加内容
addHeader(doc);
addBody(doc);
// 结束前添加 footer(多页也有效)
doc.end();
doc.on("end", () => {
addFooter(doc);
});
// 把后端的pdf地址返回给前端
res.json({
code: 200,
data: "http://localhost:3000/output.pdf",
});
res.send("PDF generated successfully!");
});
app.listen(3000, () => {
console.log("Server is running on port 3000");
});

浏览器缓存带来的小问题
之前自己写 demo,遇到一个小问题,可能刚刚入门的小白一时找不到原因,特此记录下:
- 后端生成pdf文件,但是名称都相同(而且由于前端通过 iframe 渲染 pdf 的url 地址,url
地址也相同)- 尽管内容不同,但是浏览器发现相同的名称pdf(静态文件),会继续使用缓存,导致后端pdf虽然重新覆盖生成,但是前端浏览器中的 iframe 渲染的 pdf 还是旧的(缓存中的)。

解决方法是:
js
<iframe :src="url" width="100%" height="1200px"></iframe>
const onSubmit = () => {
axios.post("http://localhost:3000/form2pdf", form).then((res) => {
url.value = res.data.data + "?t=" + Date.now();
console.log(url);
});
};
添加一个时间戳来阻止浏览器继续使用缓存,以保证每次重新请求都可以渲染最新的pdf 文件数据。


- 浏览器为什么会缓存 PDF?
静态资源(如 PDF、图片、JS、CSS)默认会被浏览器缓存。
这是因为 Express 的 express.static()
中间件会自动为静态资源设置缓存相关的 HTTP 头(如 Cache-Control
),让浏览器下次访问同样的 URL 时直接用本地缓存,加快加载速度、减少服务器压力。
只要 URL 一样,且没有特殊的禁止缓存设置,浏览器就会优先用缓存。
如果在 /form2pdf
路由里加了:
js
res.setHeader("Cache-Control", 'no-store');
但这个 header 只作用于 /form2pdf
这个接口的响应,对 output.pdf
这个静态文件的响应没有影响 。
output.pdf
的响应是由 express.static(__dirname)
处理的,和接口响应头无关。
- 为什么加参数能解决?
当你访问 output.pdf?t=xxx
时,浏览器认为这是一个全新的资源(即使实际文件一样),所以会重新请求服务器,不会用缓存。
- 如何让静态 PDF 不被缓存?
可以让 Express 静态资源响应时加上禁止缓存的 header:
js
app.use(express.static(__dirname, {
setHeaders: (res, path) => {
if (path.endsWith('.pdf')) {
res.setHeader('Cache-Control', 'no-store');
}
}
}));
这样浏览器每次都会重新请求 PDF 文件。