Fetch POST请求中二进制字符串格式文件属性丢失问题排查
问题:前端POST请求中文件二进制字符串字段在后端丢失
前端通过Fetch发送POST请求,将文件经FileReader转为二进制字符串后放入请求的JSON对象中。前端console.log显示包含file属性的完整数据,但后端接收req.body时,materiales数组内的对象丢失了file属性,仅能获取其他字段。
前端代码
var fileReader = new FileReader(); $('#form').on('submit', function (event) { event.preventDefault() var data = {} data.materiales = [] data.fecha = new Date().toISOString().split('.')[0] var file = {} $("#formHeader :input,select").each(function(){ if ($(this).val() != ''){ data[$(this).attr('name')] = $(this).val() } }); $("[name = materiales]").each(function(){ var obj = {} $(this).find('input,select,textarea').each(function(){ if ($(this).val() != ''){ if ($(this).attr('name') == "pdf"){ fileReader.onload = function(){ obj.file = fileReader.result.toString()} fileReader.readAsBinaryString( $(this).prop('files')[0]) } else obj[$(this).attr('name')] = $(this).val() } }) data.materiales.push(obj) }); console.log(data) fetch("/vale", { method: "POST", body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' } }).then(response => { if (response.ok) { return response.json(); } throw new Error(response.statusText); }) .then(responseJSON => { window.location.href = "/ordenes-compra/" }) .catch(err => { alert(err.message) }) })
前端console.log输出
{ "materiales": [ { "codigo": "423423", "descripcion": "Prueba", "cantidad": "55", "file": "%PDF-1.4\n%âãÏÓ\n1 0 obj\n<<Type/XObject/ColorSpace/DeviceRGB/Subtype/Image/BitsPerComponent 8/Width 1824/Length 161263/Height 695/Filter/DCTDecode>>stream\nÿØÿà\u0000" } ], "fecha": "2022-08-25T15:20:00", "pedidoPara": "Almacen", "proyecto": "ABS", "departamento": "Armado" }
后端代码
router.post('/vale/', async(req, res) => {; console.log(req.body.materiales) });
后端console.log输出
[ { codigo: '423423', descripcion: 'Prueba', cantidad: '55' } ]
尝试访问req.body.materiales[0].file返回undefined。
原因分析
FileReader.readAsBinaryString是异步操作,onload回调会在文件读取完成后才执行。当前代码中,触发文件读取后立刻把obj推入数组并发送请求——此时文件还没读取完成,obj.file根本还没被赋值。
前端console.log显示file属性是因为:console输出的是对象引用,后续fileReader完成赋值后,console里的对象会被更新显示,但实际发送给后端的JSON是在赋值前就完成stringify的,自然没有file字段。
解决方案
用Promise封装文件读取操作,等待所有文件读取完成后再发送请求:
$('#form').on('submit', function (event) { event.preventDefault() var data = {} data.materiales = [] data.fecha = new Date().toISOString().split('.')[0] $("#formHeader :input,select").each(function(){ if ($(this).val() != ''){ data[$(this).attr('name')] = $(this).val() } }); // 收集所有文件读取的Promise const readFilePromises = []; $("[name = materiales]").each(function(){ var obj = {} $(this).find('input,select,textarea').each(function(){ if ($(this).val() != ''){ if ($(this).attr('name') == "pdf"){ // 用Promise封装文件读取 const readPromise = new Promise((resolve) => { const fileReader = new FileReader(); fileReader.onload = function(){ obj.file = fileReader.result.toString(); resolve(); }; fileReader.readAsBinaryString( $(this).prop('files')[0]); }); readFilePromises.push(readPromise); } else { obj[$(this).attr('name')] = $(this).val() } } }) data.materiales.push(obj) }); // 等待所有文件读取完成后再发送请求 Promise.all(readFilePromises).then(() => { console.log(data); fetch("/vale", { method: "POST", body: JSON.stringify(data), headers: { 'Content-Type': 'application/json' } }).then(response => { if (response.ok) { return response.json(); } throw new Error(response.statusText); }).then(responseJSON => { window.location.href = "/ordenes-compra/" }).catch(err => { alert(err.message) }); }); })
额外建议:二进制字符串转JSON可能存在编码问题,更推荐用FileReader.readAsDataURL把文件转成Base64字符串,兼容性更好且不会出现编码丢失的情况。
内容的提问来源于stack exchange,提问作者Adrian Gonzalez
相关产品推荐
相关产品推荐

