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

Angular文件上传至Spring Boot失败:multipart boundary被拒绝

问题修复方案

核心错误原因

  1. 手动设置Content-Type导致丢失boundary:使用FormData发送请求时,浏览器会自动生成包含boundary的multipart/form-data请求头(格式类似multipart/form-data; boundary=----WebKitFormBoundaryXXXX),手动设置固定的Content-Type会覆盖这个自动生成的头,服务器无法识别请求边界,从而抛出no multipart boundary was found错误。
  2. FormData中数组的append方式错误:直接将整个photos数组append到FormData中,Spring无法解析成List<Photo>对象,需要按照嵌套对象的格式逐个添加属性。
  3. 图片数据格式错误:如果上传时用event.target.result存储的是DataURL,需要转换为Blob/File对象才能被Spring的MultipartFile接收。

分步修复

1. 修改请求方法,移除手动设置的Content-Type

postRequest<T>(url: string, body: any): Observable<HttpResponse<T>> {
  return this.http.post<T>(url, body, {
    observe: "response",
    withCredentials: true,
    // 移除手动设置的Content-Type,HttpClient会自动处理
  });
}

2. 正确构造FormData(处理photos数组)

情况一:上传时直接存储File对象(推荐)

先修改图片上传逻辑,直接保存选择的File对象,而非DataURL:

// 假设是通过input[type=file]选择文件
onFileSelected(event: Event) {
  const file = (event.target as HTMLInputElement).files?.[0];
  if (!file) return;

  const photosArray = this.postForm.get("photos") as FormArray;
  const newPhotoGroup = this.fb.group({
    id: "0", // 初始值设为0,对应Photo的默认id
    file: file, // 直接存储File对象
  });
  photosArray.push(newPhotoGroup);
}

然后构造FormData:

const formData = new FormData();
formData.append("title", this.postForm.get("title")?.value);

// 遍历photos表单数组,按Spring要求的格式添加属性
const photosArray = this.postForm.get("photos") as FormArray;
photosArray.controls.forEach((photoGroup, index) => {
  formData.append(`photos[${index}].id`, photoGroup.get("id")?.value);
  formData.append(`photos[${index}].file`, photoGroup.get("file")?.value);
});

情况二:已使用DataURL存储图片

如果必须用DataURL,需要先转换为Blob对象:

// 新增DataURL转Blob的辅助函数
function dataURLToBlob(dataURL: string): Blob {
  const [mimePart, dataPart] = dataURL.split(',');
  const mime = mimePart.match(/:(.*?);/)![1];
  const rawData = atob(dataPart);
  const uint8Array = new Uint8Array(rawData.length);
  
  for (let i = 0; i < rawData.length; i++) {
    uint8Array[i] = rawData.charCodeAt(i);
  }
  
  return new Blob([uint8Array], { type: mime });
}

// 构造FormData时转换
const formData = new FormData();
formData.append("title", this.postForm.get("title")?.value);

const photosArray = this.postForm.get("photos") as FormArray;
photosArray.controls.forEach((photoGroup, index) => {
  const dataURL = photoGroup.get("file")?.value;
  const blob = dataURLToBlob(dataURL);
  
  formData.append(`photos[${index}].id`, photoGroup.get("id")?.value || "0");
  // 第三个参数可选,用于指定文件名
  formData.append(`photos[${index}].file`, blob, `photo-${index}.jpg`);
});

3. 验证Spring端配置(可选)

确保Spring Boot已启用multipart支持(默认已启用,若有自定义配置需检查):

spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 10MB # 根据需求调整
      max-request-size: 10MB

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 12:55:58