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

Angular向Node.js Multer上传图片时后端接收文件为undefined

Multer 上传文件返回 undefined 修复方案

核心原因是前端未按文件上传要求封装请求,且前后端字段配置不匹配,按以下步骤修改即可:

1. 修复前端请求格式问题

Multer 仅能解析 multipart/form-data 格式的请求体,原代码直接发送 JSON 格式数据,文件无法被识别。

修改 bookdata.service.ts

使用 FormData 封装所有参数,不要手动设置 Content-Type 请求头,浏览器会自动补全正确的分隔符参数:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http'
import {BooksModel} from '../components/books/books.model';

@Injectable({
  providedIn: 'root'
})
export class BookdataService {
  constructor(private http:HttpClient) { }
  getBooks(){
    return this.http.get("http://localhost:3000/books");
  }
  newBook(item: BooksModel){
    const formData = new FormData();
    // 注意:文件字段名必须和后端 Multer 配置的字段名一致,这里为 image
    formData.append('image', item.bookImage);
    formData.append('bookName', item.bookName);
    formData.append('bookAuthor', item.bookAuthor);
    formData.append('bookCategory', item.bookCategory);
    formData.append('bookDescription', item.bookDescription);
    // 直接返回请求对象,不在 service 里订阅
    return this.http.post("http://localhost:3000/books/insert", formData)
  }
}

注意:不要手动添加 Content-Type: multipart/form-data 请求头,手动设置会丢失浏览器自动生成的 boundary 参数,导致 Multer 解析失败。

2. 修复 TS 类型定义错误

原代码将文件字段定义为 string 类型,和实际的 File 对象类型不匹配,修改 Angular 端数据模型 books.model.ts:

export class BooksModel{
   constructor(
       public bookName: string,
       public bookAuthor: string,
       public bookCategory: string,
       public bookDescription: string,
       public bookImage : File // 修改为 File 类型
   ){}
}

3. 修复前端提交逻辑

原代码提交后立刻跳转页面,不等接口返回,容易导致请求中断,修改 addbook.component.ts:

export class AddbookComponent implements OnInit {
  title:String="Publish Your Book";
  constructor(private bookdataService: BookdataService,  private router: Router) { }
  bookItem= new BooksModel("","","","",null);
  ngOnInit(): void {
  }
  AddBook()
  {
    if(!this.bookItem.bookImage){
      alert('请先上传书籍封面');
      return
    }
    // 等接口返回成功后再跳转、提示
    this.bookdataService.newBook(this.bookItem).subscribe({
      next: (res)=>{
        console.log("called");
        console.log(this.bookItem.bookImage);
        alert("发布成功");
        this.router.navigate(['/']);
      },
      error: (err)=>{
        alert('发布失败:'+err.message)
      }
    })
  }
 
  upload(event: any){
    const file=event.target.files[0];
    console.log(file);  
    this.bookItem.bookImage=file;
  }
}

4. 修复后端路由取值逻辑

原代码从 req.body.book 里取值,和前端传的字段结构不匹配,且硬编码了图片 contentType,修改 addbookRoutes.js 的 /insert 路由:

booksRouter.post('/insert',upload.single(`image`),function(req,res){
  res.header("Access-Control-Allow-Origin","*")
  res.header('Access-Control-Allow-Methods: GET,POST,PATCH,PUT,DELETE')
  console.log(req.body);
  console.log(req.file);
  // 增加空值校验,避免未传文件时报错
  if(!req.file){
    return res.status(400).send({msg: '请上传封面图片'})
  }
  const book = {       
      bookName : req.body.bookName,
      bookAuthor : req.body.bookAuthor,
      bookCategory : req.body.bookCategory,
      bookDescription : req.body.bookDescription,
      bookImage: {
        data: fs.readFileSync(path.join('./public/uploads/' + req.file.filename)), 
        contentType: req.file.mimetype, // 用上传文件自带的MIME类型,不要硬编码
      }
  }       
  const newBook = new Bookdata(book);
  newBook.save().then(()=>{
    res.send({msg: '书籍发布成功'})
  }).catch(err=>{
    res.status(500).send({msg: '保存失败', error: err})
  });
});

验证方法

改完后打开浏览器开发者工具的 Network 面板,查看上传请求:

  • 请求 Payload 格式为 multipart/form-data
  • 能看到名为 image 的文件条目
  • 后端控制台打印 req.file 可看到完整文件信息,即代表上传正常。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 05:49:18