Web-图片上传出现的错误

发布于:2025-06-06 ⋅ 阅读:(23) ⋅ 点赞:(0)

前后端数据格式的交互方式不匹配


​问题根源​

  1. ​后端期望的格式​​(LostItemController.java):

    • 使用 @RequestPart LostItemRequestDTO request 接收 ​​JSON 对象​​(自动反序列化)
    • 使用 @RequestPart MultipartFile image 接收 ​​文件​
    • 要求 Content-Type: multipart/form-data
  2. ​前端实际发送的格式​​:

    • 将 JSON 数据以字符串形式放在 request 字段(formData.append('request', JSON.stringify(requestData))
    • 后端无法自动反序列化 request 字段中的 JSON 字符串,导致 ​​415 错误​

解决方案​

​方案1:修改前端 - 直接发送对象而非 JSON 字符串​

前端不需要手动 JSON.stringify,直接让 FormData 按字段拆分

async submitForm() {
  const formData = new FormData();
  
  // 直接添加字段(不要包裹成 JSON 字符串)
  formData.append('category', this.formData.category);
  formData.append('description', this.formData.description);
  formData.append('lostLocation', this.formData.lostLocation);
  formData.append('lostTime', new Date(this.formData.lostTime).toISOString());
  
  // 添加图片
  if (this.formData.image) {
    formData.append('image', this.formData.image);
  }

  try {
    const response = await fetch('/api/lost', {
      method: 'POST',
      body: formData, // 不要手动设置 Content-Type!
    });
    // 处理响应...
  } catch (error) {
    alert('提交失败: ' + error.message);
  }
}

方案2:修改后端 - 手动解析 JSON 字符串​

如果必须保持前端发送 request 字段,后端需调整:

@PostMapping(value = "/lost", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result add(
    @RequestPart String request,  // 改为接收字符串
    @RequestPart(required = false) MultipartFile image
) {
    // 手动解析 JSON
    LostItemRequestDTO requestData = new ObjectMapper().readValue(request, LostItemRequestDTO.class);
    // 后续逻辑...
}

后端无法反序列化前端发送的 LostItemRequestDTO 对象​​,具体错误为:

Cannot construct instance of `com.text.pojo.LostItemRequestDTO` 
(no Creators, like default constructor, exist)

确保前后端字段名严格一致(尤其是 image

问题根源​

  1. ​后端问题​​:

    • LostItemRequestDTO 类缺少 ​​默认构造函数​​ 或 ​​Jackson 反序列化所需的注解​​。
    • Spring 无法将前端传来的 JSON 数据自动转换为 LostItemRequestDTO 对象。
  2. ​前端问题​​:

    • 当前前端发送的是 ​FormData​,但后端期望的是 ​multipart/form-data 中的 JSON 对象​​(@RequestPart LostItemRequestDTO request

解决办法:

修复后端 DTO 类(推荐)​

在 LostItemRequestDTO 中添加 ​​无参构造函数​​ 和 ​​Setter 方法​

package com.text.pojo;

public class LostItemRequestDTO {
    private String category;
    private String description;
    private String lostLocation;
    private String lostTime;

    // 1. 必须添加无参构造函数
    public LostItemRequestDTO() {}

    // 2. 必须提供所有字段的 getter/setter
    public String getCategory() { return category; }
    public void setCategory(String category) { this.category = category; }

    // 其他字段的 getter/setter...
}

方案2:修改后端控制器(手动解析 JSON)​

如果无法修改 DTO,可以手动解析 JSON:

@PostMapping(value = "/lost", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result add(
    @RequestPart String request,  // 改为接收字符串
    @RequestPart(required = false) MultipartFile image
) {
    // 手动解析 JSON
    ObjectMapper objectMapper = new ObjectMapper();
    LostItemRequestDTO requestData = objectMapper.readValue(request, LostItemRequestDTO.class);
    // 后续逻辑...
}

方案3:调整前端请求格式​

如果后端坚持用 @RequestPart LostItemRequestDTO request,前端需要 ​​按字段拆分 FormData​​(不推荐,但可行):

const formData = new FormData();
formData.append('category', this.formData.category);
formData.append('description', this.formData.description);
formData.append('lostLocation', this.formData.lostLocation);
formData.append('lostTime', this.formData.lostTime);
formData.append('image', this.formData.image);

问题根源​

  1. LocalDateTime 反序列化失败​​:

    • 前端发送的 lostTime 是字符串(如 "2025-06-05T20:06:13"),但后端无法自动将其转换为 LocalDateTime
    • 需要为 LocalDateTime 配置自定义反序列化器。
  2. ​DTO 构造函数冲突​​:

    • 虽然提供了无参构造函数,但还存在全参构造函数,可能导致 Jackson 混淆。

​解决方案​

​1. 修复 DTO 类​

package com.text.pojo;

import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import java.time.LocalDateTime;

public class LostItemRequestDTO {
    private String category;
    private String description;
    private String lostLocation;

    @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") // 指定日期格式
    @JsonDeserialize(using = LocalDateTimeDeserializer.class) // 使用反序列化器
    private LocalDateTime lostTime;

    // 只保留无参构造函数
    public LostItemRequestDTO() {}

    // 移除全参构造函数(避免干扰 Jackson)

    // getters/setters...
}
2. 修改前端时间格式​

确保前端发送的时间字符串格式与 @JsonFormat 一致:

// 前端代码(格式化时间)
const requestData = {
  category: this.formData.category,
  description: this.formData.description,
  lostLocation: this.formData.lostLocation,
  lostTime: new Date(this.formData.lostTime).toISOString().slice(0, 19) // 格式:YYYY-MM-DDTHH:mm:ss
};

formData.append('request', JSON.stringify(requestData));
  1. jackson 的默认行为​​:

    • 优先尝试用无参构造函数 + setter(成功)
    • ​但如果类中只有全参构造函数​​(没有无参构造),Jackson 会尝试用它,此时必须满足:
      • 参数名称 ​​严格匹配​​ JSON 字段名
      • 或使用 @JsonProperty 标注参数
  2. ​您的实际错误​​:

    • 虽然提供了无参构造函数,但全参构造函数的存在 ​​干扰了 Jackson 的决策​​(尤其在高版本 Jackson 中)
    • 更关键的是:LocalDateTime 字段没有配置反序列化规则(主因)

网站公告

今日签到

点亮在社区的每一天
去签到