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

Angular向Node.js上传文件时文件数据为null的问题求助

问题:Node.js后端接收Angular上传文件时始终返回null

我正在开发Node.js后端与Angular前端的项目,前端发送POST请求后,后端可正常接收普通数据,但上传的文件数据始终显示为null。

相关截图:
前端请求详情
文件上传控件截图


后端代码(Node.js + multer)

const imageUpload = multer({
    storage: multer.diskStorage({
        destination: function (req, file, cb) {
            console.log("asd");
            cb(null, "Attachments/");
        },
        filename: function (req, file, cb) {
            cb(null, new Date().valueOf() + "_" + file.originalname);
        },
    }),

    limits: { fileSize: 10000000 },
    fileFilter: function (req, file, cb) {
        if (
            !file.originalname.match(
                /\.(jpg|JPG|webp|jpeg|JPEG|png|PNG|gif|GIF|pdf|PDF|docx|doc)$/
            )
        ) {
            req.fileValidationError = "Only Image, PDF and Word files are allowed!";
            return cb(null, false);
        } else {
            req.fileValidationError = "";
        }
        cb(null, true);
    },
});

// Add Property Details
app.post('/addproperty', imageUpload.single("image"), (req, res) => {
    console.log("vicky", req.body.body.file);
    if (req.fileValidationError != "") {
        throw req.fileValidationError;
    }
    console.log(req.body.body)
    req.body.body = req.body;
    let qrr = 'INSERT INTO listproperty (Type,  plotNo, location,  sqft, sellType,  facing, price, BedRoom, BAthRoom,  Parking, date, Description, src)VALUES("'+req.body.Type+'","'+req.body.plotNo+'","'+req.body.location+'","'+req.body.sqft+'","'+req.body.sellType+'","'+req.body.facing+'", "'+req.body.price+'","'+req.body.BedRoom+'","'+req.body.BAthRoom+'","'+req.body.Parking+'","'+req.body.date+'","'+req.body.Description+'","'+req.body.src+'");'

    db.query(qrr, (err1, res1) => {
        if(err1)console.log(err1);
        else {
            res.send({
                message: 'Data inserted',
                body:res1
            })
        }
    })
});

前端代码(Angular)

newProperty : any = new FormGroup({
    Type: new FormControl([Validators.required]),
    sellType: new FormControl([Validators.required]),
    facing: new FormControl([Validators.required]),
    src: new FormControl(''),
    plotNo: new FormControl('', Validators.required),
    location: new FormControl('', Validators.required),
    price: new FormControl(Validators.required),
    sqft: new FormControl(Validators.required),
    BedRoom: new FormControl(Validators.required),
    BAthRoom: new FormControl(Validators.required),
    Parking: new FormControl(Validators.required),
    Description: new FormControl('', Validators.required),
    date: new FormControl([Validators.required]),
});

// On file Select
onChange(event: any) {
    this.newProperty.value.src  = event.target.files;
    //console.log(event);
    
    // this.file = event.target.files[0];
}
ngOnInit(): void {
    this.getPropertyType()
    this.sellType = this.data.getSellType();
    let x = this.route.snapshot.paramMap.get('property')
    
    // if (x) {
    //   var d=this.data.getSingleData(x)
    //   console.log(d);
    //   setTimeout(() => {
    //     this.newProperty.patchValue(d)
    //     // this.newProperty.setControl('src')
    //   }, 1000);
    // }
}
getPropertyType() {
    console.log("res", this.sql.getPlotType());
    this.sql.getPlotType().subscribe((res)=>{
        console.log(res);
        for (let i = 0; i < res.length; i++) {
            this.plotType.push( res[i].propertyType);
        }
    // this.plotType =res  
    });
}
addNew() {
    if (this.newProperty.invalid) {
        alert('fill all details');
        return;
    }
    var data = [];
    //JSON.parse(this.newProperty.value)
    this.sql.addplotdata(this.newProperty.value).subscribe((res)=>{
        console.log(res);
        for (let i = 0; i < res.length; i++) {
            data.push( res);
        }
        console.log("data");
    // this.plotType =res  
    });
    
    console.log(this.newProperty.value);
    // this.data.addData(this.newProperty.value);
    //this.newProperty.reset();
    this.router.navigate(['add-property']);
}

addplotdata(value: any): Observable<any> {
    return this.http.post(this.addPropUrl, {value, "image" :value.src});
} // Backend.ts

问题原因及修复方案

核心问题

  1. 前端请求格式错误:文件上传必须使用FormData格式,你当前用HttpClient.post发送JSON数据,multer无法从JSON中解析文件。
  2. 文件赋值方式错误:直接修改this.newProperty.value.src不会触发表单控件的更新,且你赋值的是FileList而非单个File对象。
  3. 后端参数读取错误:multer处理单文件上传后,文件数据存储在req.file中,而非req.body,你当前代码错误地从req.body.body.file读取。
  4. SQL注入风险:原SQL语句直接拼接字符串,存在严重安全隐患。

修复步骤

前端修改
  1. 修正文件选择事件:
onChange(event: any) {
    const file = event.target.files[0]; // 取单个文件,对应后端single上传
    this.newProperty.patchValue({ src: file });
    this.newProperty.get('src')?.markAsDirty(); // 手动标记控件为脏
}
  1. 用FormData发送请求:
addplotdata(formValue: any): Observable<any> {
    const formData = new FormData();
    // 追加普通表单字段
    Object.keys(formValue).forEach(key => {
        if (key !== 'src' && formValue[key]) {
            formData.append(key, formValue[key]);
        }
    });
    // 追加文件,字段名与后端multer.single("image")一致
    if (formValue.src) {
        formData.append('image', formValue.src);
    }
    return this.http.post(this.addPropUrl, formData);
}
  1. 修正表单控件定义:
src: new FormControl(null) // 文件控件无需初始字符串值
后端修改
  1. 正确读取文件和表单数据:
app.post('/addproperty', imageUpload.single("image"), (req, res) => {
    if (req.fileValidationError) {
        return res.status(400).send({ error: req.fileValidationError });
    }

    console.log('上传文件:', req.file);
    console.log('表单数据:', req.body);

    // 使用参数化查询避免SQL注入
    const qrr = `INSERT INTO listproperty 
        (Type, plotNo, location, sqft, sellType, facing, price, BedRoom, BAthRoom, Parking, date, Description, src)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;

    const values = [
        req.body.Type, req.body.plotNo, req.body.location, req.body.sqft,
        req.body.sellType, req.body.facing, req.body.price, req.body.BedRoom,
        req.body.BAthRoom, req.body.Parking, req.body.date, req.body.Description,
        req.file ? req.file.filename : '' // 存储文件名,根据需求调整为路径或其他
    ];

    db.query(qrr, values, (err1, res1) => {
        if (err1) {
            console.error(err1);
            return res.status(500).send({ error: '数据插入失败' });
        }
        res.send({
            message: 'Data inserted',
            body: res1
        })
    })
});

内容的提问来源于stack exchange,提问作者romil_stage

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 21:00:58