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
相关产品推荐
相关产品推荐

