如何在Java REST客户端接收Angular传来的Blob数据
问题分析
你遇到的核心问题有两个:
java.sql.Blob是抽象类,没有默认构造器,Jackson无法直接实例化它;- 前端将Blob放入JSON payload发送时,浏览器会把Blob序列化成仅包含
size和type的普通对象,根本没有传输实际的二进制PDF数据,这才是导致反序列化失败的根源,添加@JsonTypeInfo无法解决这个本质问题。
下面给出两种可行的解决方案,优先推荐第一种。
方案一:使用FormData传输(最推荐,标准文件上传方式)
FormData是浏览器专门用来传输文件和表单数据的格式,Spring对它的支持非常完善,完全避免JSON传输二进制的问题。
前端(Angular)修改
修改服务中的submitOrderedItems方法,用FormData分别封装Order对象(转成JSON字符串)和PDF Blob:
public submitOrderedItems(order: Order, pdf: Blob) { console.log("submitting ordered items"); const url = "http://localhost:8080/orders/add"; const formData = new FormData(); // 将Order对象序列化为JSON字符串 formData.append('order', JSON.stringify(order)); // 添加PDF Blob,指定文件名和MIME类型 formData.append('pdf', pdf, 'order_' + Date.now() + '.pdf'); this.http.post(url, formData).subscribe(value => {}); console.log("sended"); }
后端(Spring)修改
- 废弃
OrderAndBlob类,控制器方法改用@RequestParam接收FormData中的参数:
@RestController @RequestMapping("/orders") @CrossOrigin(origins = "*", maxAge = 3600) public class OrderController { @Autowired private ObjectMapper objectMapper; // 用于解析JSON字符串为Order对象 @Autowired private OrderService orderService; @PostMapping("/add") @PreAuthorize("hasRole('USER')") public void addOrders(@RequestParam("order") String orderJson, @RequestParam("pdf") MultipartFile pdfFile) throws IOException, SQLException { // 将JSON字符串转为Order对象 Order order = objectMapper.readValue(orderJson, Order.class); // 若需要将MultipartFile转为java.sql.Blob,可通过以下方式实现 Blob pdfBlob = new SerialBlob(pdfFile.getBytes()); System.out.println(pdfBlob); orderService.saveOrderAndLines(order); // 这里可以添加PDF Blob的持久化逻辑 } }
方案二:Base64编码传输(JSON兼容方式)
如果必须用JSON payload传输,可将Blob转为Base64字符串,后端接收后再解码为二进制数据并转为Blob。
前端(Angular)修改
- 在生成Blob后,通过
FileReader将其转为Base64字符串:
confirmOrder(): void { let DATA: any = document.body; html2canvas(DATA).then((canvas) => { let fileWidth = 208; let fileHeight = (canvas.height * fileWidth) / canvas.width; let PDF = new jsPDF('p', 'mm', 'a4'); let position = 0; PDF.addImage(canvas, 'PNG', 0, position, fileWidth, fileHeight); this.pdfBlob = PDF.output('blob'); // 将Blob转为Base64字符串(去掉前缀,只保留编码部分) const reader = new FileReader(); reader.onload = () => { const fullBase64 = reader.result as string; const pdfBase64 = fullBase64.split(',')[1]; this.salesproductService.submitOrderedItems(this.order, pdfBase64); this.router.navigate(["/main"]); }; reader.readAsDataURL(this.pdfBlob); }); }
- 修改服务方法,发送包含Order和Base64字符串的JSON payload:
public submitOrderedItems(order: Order, pdfBase64: string) { console.log("submitting ordered items"); const url = "http://localhost:8080/orders/add"; const payload = { order: order, pdfBase64: pdfBase64 }; this.http.post(url, payload).subscribe(value => {}); console.log("sended"); }
后端(Spring)修改
- 修改
OrderAndBlob类,改为接收Base64字符串,再提供转为Blob的方法:
public class OrderAndBlob { private Order order; private String pdfBase64; // Getter和Setter public Order getOrder() { return order; } public void setOrder(Order order) { this.order = order; } public String getPdfBase64() { return pdfBase64; } public void setPdfBase64(String pdfBase64) { this.pdfBase64 = pdfBase64; } // 将Base64字符串转为java.sql.Blob public Blob getPdfBlob() throws SQLException { byte[] pdfBytes = Base64.getDecoder().decode(pdfBase64); return new SerialBlob(pdfBytes); } }
- 控制器方法保持
@RequestBody,调用转换方法获取Blob:
@RestController @RequestMapping("/orders") @CrossOrigin(origins = "*", maxAge = 3600) public class OrderController { @Autowired private OrderService orderService; @PostMapping("/add") @PreAuthorize("hasRole('USER')") public void addOrders(@RequestBody OrderAndBlob orderAndBlob) throws SQLException { Blob pdfBlob = orderAndBlob.getPdfBlob(); System.out.println(pdfBlob); orderService.saveOrderAndLines(orderAndBlob.getOrder()); } }
内容的提问来源于stack exchange,提问作者Yannick Mussche
相关产品推荐
相关产品推荐

