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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 00:54:32