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

ReactJS转图片为字节数组上传Spring Boot后端报错问题

解决React上传图片到Spring Boot并存储bytea到PostgreSQL的序列化错误

错误原因分析

你遇到的JSON解析错误,核心问题在于请求类型与控制器接收方式不匹配:

  • 前端发送的是multipart/form-data类型的表单请求,但后端控制器用@RequestBody接收,这个注解是用来解析JSON格式请求体的,无法处理表单数据。
  • 即使你把图片转成字节数组封装,Jackson也无法将封装后的对象反序列化为byte[]类型,因为它期望的是字节数组的JSON表示(比如base64字符串或数字数组),但实际收到的是对象结构,因此抛出Cannot deserialize instance of [B out of START_OBJECT token错误。

推荐解决方案:使用MultipartFile接收文件

这种方式更符合文件上传的标准流程,且效率更高,不需要额外的字节数组转换操作。

1. Spring Boot后端修改

控制器调整

将控制器改为接收MultipartFile(对应图片文件)和普通参数(对应公司名称),再手动封装到Company模型中:

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

@RestController
public class CompanyController {

    private final CompanyService companyService;

    // 构造注入service
    public CompanyController(CompanyService companyService) {
        this.companyService = companyService;
    }

    @PostMapping("/companies")
    public ResponseEntity<Company> createCompany(
            @RequestParam("name") String name,
            @RequestParam("logo") MultipartFile logoFile
    ) throws IOException {
        Company company = new Company();
        company.setName(name);
        // 将MultipartFile直接转为byte[],适配PostgreSQL的bytea类型
        company.setLogo(logoFile.getBytes());
        
        Company savedCompany = companyService.save(company);
        return ResponseEntity.ok(savedCompany);
    }
}

模型类保持不变

你的Company模型无需修改,byte[] logo可以直接对应PostgreSQL的bytea类型:

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class Company {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private byte[] logo;

    // getter、setter、构造方法
}

2. React前端修改

无需手动将图片转成字节数组,直接用FormData封装文件和名称,发送multipart/form-data请求:

const handleCompanySubmit = async (e) => {
    e.preventDefault();
    
    // 获取表单中的名称和文件(假设你有对应的输入框和文件选择器)
    const companyName = document.getElementById('companyName').value;
    const selectedFile = document.getElementById('logoFile').files[0];

    const formData = new FormData();
    formData.append('name', companyName);
    formData.append('logo', selectedFile);

    try {
        const response = await fetch('/api/companies', {
            method: 'POST',
            body: formData
            // 无需手动设置Content-Type,浏览器会自动添加正确的multipart/form-data及边界标识
        });
        
        if (response.ok) {
            const savedCompany = await response.json();
            console.log('公司创建成功:', savedCompany);
        } else {
            console.error('请求失败');
        }
    } catch (error) {
        console.error('上传出错:', error);
    }
};

可选方案:用Base64字符串传输(不推荐大文件)

如果一定要用JSON格式传输,可以将图片转为Base64字符串,后端再解码为byte[]:

React前端修改

// 将文件转为Base64字符串(去掉前缀,只保留编码部分)
const fileToBase64 = (file) => {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.readAsDataURL(file);
        reader.onload = () => resolve(reader.result.split(',')[1]);
        reader.onerror = (err) => reject(err);
    });
};

const handleJsonSubmit = async (e) => {
    e.preventDefault();
    const companyName = document.getElementById('companyName').value;
    const selectedFile = document.getElementById('logoFile').files[0];
    
    const base64Logo = await fileToBase64(selectedFile);
    const companyData = { name: companyName, logo: base64Logo };

    try {
        const response = await fetch('/api/companies/json', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(companyData)
        });
        
        const savedCompany = await response.json();
        console.log('公司创建成功:', savedCompany);
    } catch (error) {
        console.error('上传出错:', error);
    }
};

Spring Boot后端修改

新建一个请求DTO接收Base64字符串,再解码为byte[]:

// 请求DTO
public class CompanyRequest {
    private String name;
    private String logo; // 存储Base64编码的字符串

    // getter、setter
}

// 控制器方法
@PostMapping("/companies/json")
public ResponseEntity<Company> createCompanyWithJson(@RequestBody CompanyRequest request) {
    Company company = new Company();
    company.setName(request.getName());
    // 将Base64字符串解码为byte[]
    company.setLogo(Base64.getDecoder().decode(request.getLogo()));
    
    Company savedCompany = companyService.save(company);
    return ResponseEntity.ok(savedCompany);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 11:52:25