Node.js实现PDF Buffer转回PDF及Express服务端保存前端上传PDF
嘿,我来帮你解决这个PDF保存的问题!
你当前遇到的核心问题是:body-parser的urlencoded中间件没法处理二进制的PDF Blob数据——它专门用来解析表单编码的文本数据,对二进制流无能为力。下面给你三种可行的解决方案,你可以根据自己的需求选择:
方案1:直接处理请求流(不用改前端代码)
这种方式最贴合你现有的前端实现,后端直接读取请求的原始数据流,写入文件即可:
const express = require('express'); const fs = require('fs'); const path = require('path'); const app = express(); // 先确保保存文件的目录存在 const uploadDir = path.join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } // 处理/upload路由,不要用body-parser解析这个请求 app.post('/upload', (req, res) => { const savePath = path.join(uploadDir, 'output.pdf'); const writeStream = fs.createWriteStream(savePath); // 把请求的数据流导向文件写入流 req.pipe(writeStream); // 监听写入完成事件 writeStream.on('finish', () => { res.status(200).send('PDF文件已成功保存!'); }); // 监听写入错误 writeStream.on('error', (err) => { console.error('保存文件出错:', err); res.status(500).send('文件保存失败,请重试'); }); }); app.listen(3000, () => { console.log('服务器运行在 http://localhost:3000'); });
注意:如果你之前全局注册了body-parser(比如app.use(bodyParser.urlencoded(...))),一定要确保这个/upload路由在body-parser注册之前定义,或者只给需要的路由应用body-parser,不然body-parser会先消耗掉请求流,导致我们拿不到数据。
方案2:用body-parser的raw模式(前端无需修改)
如果你还是想继续用body-parser,可以配置它的raw模式,让它直接把二进制数据解析到req.body里:
const express = require('express'); const bodyParser = require('body-parser'); const fs = require('fs'); const path = require('path'); const app = express(); // 配置body-parser解析application/pdf类型的二进制数据,限制文件大小(比如10MB) app.use(bodyParser.raw({ type: 'application/pdf', limit: '10mb' })); const uploadDir = path.join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } app.post('/upload', (req, res) => { const savePath = path.join(uploadDir, 'output.pdf'); // 把req.body里的二进制数据写入文件 fs.writeFile(savePath, req.body, (err) => { if (err) { console.error('保存出错:', err); return res.status(500).send('保存失败'); } res.status(200).send('PDF保存成功!'); }); }); app.listen(3000, () => { console.log('服务器已启动在 http://localhost:3000'); });
方案3:用multer处理(适合复杂上传场景)
如果以后需要支持多文件上传、文件类型验证等功能,推荐用multer这个专门处理文件上传的中间件。这时候需要稍微修改前端代码,把Blob放到FormData里:
前端修改后的代码:
var blob = pdf.output('blob'); var formData = new FormData(); // 把Blob添加到FormData,第三个参数是文件名 formData.append('pdfFile', blob, 'output.pdf'); var xhr = new XMLHttpRequest(); xhr.open('post', '/upload', true); // 不用手动设置Content-Type,浏览器会自动生成正确的multipart/form-data头 xhr.send(formData);
后端代码:
const express = require('express'); const multer = require('multer'); const fs = require('fs'); const path = require('path'); const app = express(); // 配置multer的存储规则 const storage = multer.diskStorage({ destination: (req, file, cb) => { const uploadDir = path.join(__dirname, 'uploads'); if (!fs.existsSync(uploadDir)) { fs.mkdirSync(uploadDir); } cb(null, uploadDir); // 指定保存目录 }, filename: (req, file, cb) => { cb(null, 'output.pdf'); // 自定义保存的文件名,也可以用file.originalname } }); // 创建multer实例,添加文件大小限制(比如10MB) const upload = multer({ storage: storage, limits: { fileSize: 10 * 1024 * 1024 } }); // 处理上传路由,upload.single('pdfFile')对应FormData里的键名 app.post('/upload', upload.single('pdfFile'), (req, res) => { if (!req.file) { return res.status(400).send('没有检测到上传的文件'); } res.status(200).send('PDF文件已成功保存!'); }); app.listen(3000, () => { console.log('服务器运行在 http://localhost:3000'); });
这三种方案都能解决你的问题,选哪种看你的实际需求:如果只是简单的单文件上传,方案1或2足够;如果需要更灵活的上传控制,方案3更合适。
内容的提问来源于stack exchange,提问作者dram
相关产品推荐
相关产品推荐

