pdf 轉文本
主要使用 pdf-parse 這個庫,直接識別提取我們 pdf 文件中的文字。
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文件。
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 還是舊的(緩存中的)。
解決方法是:
<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
路由里加了:
res.setHeader("Cache-Control", 'no-store');
但這個 header 只作用于 /form2pdf
這個接口的響應,對 output.pdf
這個靜態文件的響應沒有影響。
output.pdf
的響應是由 express.static(__dirname)
處理的,和接口響應頭無關。
- 為什么加參數能解決?
當你訪問 output.pdf?t=xxx
時,瀏覽器認為這是一個全新的資源(即使實際文件一樣),所以會重新請求服務器,不會用緩存。
- 如何讓靜態 PDF 不被緩存?
可以讓 Express 靜態資源響應時加上禁止緩存的 header:
app.use(express.static(__dirname, {setHeaders: (res, path) => {if (path.endsWith('.pdf')) {res.setHeader('Cache-Control', 'no-store');}}
}));
這樣瀏覽器每次都會重新請求 PDF 文件。