POST请求嵌入Base64格式PDF上传后保存为空白文件问题求助
问题根因
- 前端使用
readAsBinaryString读取得到的原始二进制字符串无法直接通过JSON序列化传输:JSON默认采用UTF-8编码,二进制流中大量非UTF-8兼容字节会被转义,导致传输后的二进制数据完全损坏,这是文件体积变大、PDF打开空白的核心原因。 - 服务端直接将收到的字符串写入文件,没有做对应的编码转换,写入的内容不是合法的PDF二进制流。
现有Base64方案修复
你原本的思路是将文件转Base64嵌入JSON,只要补全编码、解码逻辑即可正常运行:
前端代码修改
将文件读取方式改为直接生成Base64格式的DataURL:
async function post(endpoint){ let CV = document.getElementById("upfile").files[0]; var reader = new FileReader(); reader.onload = (readerEvt)=>{ const base64CV = readerEvt.target.result; let xhr = new XMLHttpRequest(); let object = { name: document.getElementById("ecaName").value, email: document.getElementById("ecaEmail").value, phone: document.getElementById("ecaTel").value, class: document.getElementById("ecaClass").value, institute: document.getElementById("ecaInstitute").value, paragraph:{ experience: document.getElementById("ecaExp").value, why: document.getElementById("ecaWhyPart").value, changes: document.getElementById("ecaWhatChanges").value, }, CV: base64CV, } xhr.open("POST", `http://localhost:8080/apply/internship/${endpoint}`,true); xhr.setRequestHeader('Content-Type', 'application/json'); xhr.send(JSON.stringify(object)); }; // 替换原有readAsBinaryString方法 reader.readAsDataURL(CV); }
后端代码修改
收到Base64内容后先解码为二进制Buffer再写入文件:
app.post('/apply/internship/econoxe', async (req, res)=>{ res.sendStatus(200); let base64CV = req.body.CV; // 移除Base64前缀标识 const pureBase64 = base64CV.replace(/^data:.+;base64,/, ''); // 解码为PDF二进制Buffer const pdfBuffer = Buffer.from(pureBase64, 'base64'); fs.writeFileSync(path.join(__dirname,`../CV/${req.body.email}.pdf`), pdfBuffer); console.log(req.body); })
同路由传输JSON+文件的更优方案
multipart/form-data方案不需要单独配置路由,比Base64传输体积小30%左右,性能更高,实现方式如下:
- 先安装依赖:
npm install multer - 前端代码修改,使用FormData承载所有参数和文件:
async function post(endpoint){ let CV = document.getElementById("upfile").files[0]; const formData = new FormData(); // 填充普通字段 formData.append('name', document.getElementById("ecaName").value); formData.append('email', document.getElementById("ecaEmail").value); formData.append('phone', document.getElementById("ecaTel").value); formData.append('class', document.getElementById("ecaClass").value); formData.append('institute', document.getElementById("ecaInstitute").value); // 嵌套对象转为JSON字符串传输 formData.append('paragraph', JSON.stringify({ experience: document.getElementById("ecaExp").value, why: document.getElementById("ecaWhyPart").value, changes: document.getElementById("ecaWhatChanges").value, })); // 填充PDF文件 formData.append('CV', CV); let xhr = new XMLHttpRequest(); xhr.open("POST", `http://localhost:8080/apply/internship/${endpoint}`,true); // 不要手动设置Content-Type,浏览器会自动生成带boundary的请求头 xhr.send(formData); }
- 后端代码修改,使用multer中间件同时解析表单字段和文件:
const multer = require('multer'); // 配置文件存储规则 const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, path.join(__dirname, '../CV/')) }, filename: function (req, file, cb) { cb(null, `${req.body.email}.pdf`) } }) const upload = multer({ storage: storage }) // 原路由完全不需要修改,添加中间件即可同时处理字段和文件 app.post('/apply/internship/econoxe', upload.single('CV'), async (req, res)=>{ res.sendStatus(200); // 普通字段全部在req.body中,嵌套对象手动解析即可 const paragraph = JSON.parse(req.body.paragraph); // 文件已自动写入配置的存储路径,无需手动调用fs写入 console.log('请求参数', req.body, '文件信息', req.file); })
内容的提问来源于stack exchange,提问作者iammithani
相关产品推荐
相关产品推荐

