You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:09:51