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

Angular+Spring Boot上传图片至PostgreSQL报非multipart请求错误求助

文件上传接口报错Current request is not a multipart request排查修复

问题根因

一共3个核心问题导致报错:

  • 后端接口注解冲突:@RequestBody用于接收application/json格式的请求体,@RequestParam MultipartFile需要请求是multipart/form-data格式,二者不能在同一个接口方法上同时使用,Spring无法同时解析两种格式的请求。
  • 前端未正确构造上传请求:你在TS里直接把普通字段序列化成JSON对象发POST请求,完全没有处理选中的文件,也没有用multipart要求的FormData格式传参。模板上写的enctype="multipart/form-data"只对原生无JS的表单提交生效,用HttpClient发AJAX请求时这个属性不会生效。
  • 前端文件输入框没有绑定逻辑:文件选择框既没有加表单控件,也没有绑定change事件,用户选的文件根本没有被组件获取到,不会随请求发给后端。

修复步骤

1. 调整后端Controller代码

给接口指定消费multipart格式数据,把@RequestBody换成@ModelAttribute,让Spring自动把multipart请求里的普通字段绑定到DTO对象上:

// 注意加consumes限定接收的请求格式
@PostMapping(value = "/save", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<Invoice> addInvoice(@ModelAttribute InvoiceDTO invoice , @RequestParam("image") MultipartFile multipartFile) throws IOException {

    String fileName = StringUtils.cleanPath(multipartFile.getOriginalFilename());
    invoice.setPhoto(fileName);
    Invoice savedInvoice = invoiceService.saveInvoice(invoice);
    String uploadDir = "user-photos/" + savedInvoice.getId();
    FileUpload.saveFile(uploadDir, fileName, multipartFile);
    LOGGER.info(" invoice saved  " + invoice.getSerialNumber() + "and id : " + invoice.getId());
    return new ResponseEntity<>(savedInvoice, HttpStatus.CREATED);
}

你写的FileUpload工具类逻辑没有问题,不需要修改。

2. 修改Angular前端模板

给文件选择框绑定change事件,获取用户选中的文件:

<div class="addreq">
  <form class="form" [formGroup]="addstudentform">
    <h1 class="header">Add new invoice</h1>

    <input formControlName="serialNumber" type="number" placeholder="serialNumber"><br>
    <input formControlName="status" type="text" placeholder="status"><br>
    <input formControlName="customerSerialNumber" type="text" placeholder="customer serial number"><br>
    <input formControlName="employeeSerialNumber" type="text" placeholder="employee serial number"><br>
    <!-- 绑定文件选择事件 -->
    <input type="file" name="image" accept="image/png, image/jpeg" (change)="onFileSelected($event)" />

    <button (click)="onSave()" type="submit" class="main-btn">Save</button>
  </form>
</div>

3. 修改Angular TS业务逻辑

新增存储选中文件的属性,提交时构造FormData对象传参,不要手动设置Content-Type请求头,HttpClient会自动给FormData生成带正确boundary的multipart请求头,手动设置反而会丢失boundary导致解析失败:

export class AddInvoiceComponent implements OnInit {
  // 存储用户选中的上传文件
  selectedFile: File | null = null;

  addstudentform: FormGroup = this._formbuilder.group({
    serialNumber: ['', Validators.required],
    status: ['', Validators.required],
    customerSerialNumber: ['', Validators.required],
    employeeSerialNumber: ['', Validators.required],
  });

  constructor(private _formbuilder: FormBuilder, private _http: HttpClient) { }

  ngOnInit(): void { }

  // 处理文件选择
  onFileSelected(event: Event): void {
    const inputEl = event.target as HTMLInputElement;
    if (inputEl.files?.length) {
      this.selectedFile = inputEl.files[0];
    }
  }

  onSave(): void {
    if (!this.selectedFile) {
      alert('请先选择要上传的发票图片');
      return;
    }

    // 构造multipart请求需要的FormData
    const submitData = new FormData();
    submitData.append('serialNumber', this.addstudentform.get('serialNumber')?.value);
    submitData.append('status', this.addstudentform.get('status')?.value);
    submitData.append('customerSerialNumber', this.addstudentform.get('customerSerialNumber')?.value);
    submitData.append('employeeSerialNumber', this.addstudentform.get('employeeSerialNumber')?.value);
    // 文件字段名必须和后端@RequestParam指定的"image"一致
    submitData.append('image', this.selectedFile, this.selectedFile.name);

    this._http.post("http://localhost:8085/invoice/save", submitData).subscribe({
      next: (res) => console.log('发票保存成功', res),
      error: (err) => console.error('保存失败', err)
    });
  }
}

改完以上内容重启后端服务、刷新前端页面,就能正常上传文件、保存发票数据,文件名会正常写入数据库的photo字段,文件也会存到你指定的user-photos/{发票id}目录下。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:36:21