Spring Boot电商项目实现2-5张商品多图上传方案问询
多商品图片上传改造方案
1. 调整商品实体类(ProductEntity)
在原ProductEntity中替换单图字段,新增5个图片路径字段,用于存储2-5张图片的下载路径:
@Data @Entity public class ProductEntity { // 保留原有所有字段... // 替换原productImage字段,新增以下5个字段 private String image1; private String image2; private String image3; private String image4; private String image5; // 更新Builder,添加对应字段的构建方法 public static class ProductEntityBuilder { // 原有Builder方法保留... private String image1; private String image2; private String image3; private String image4; private String image5; public ProductEntityBuilder image1(String image1) { this.image1 = image1; return this; } public ProductEntityBuilder image2(String image2) { this.image2 = image2; return this; } public ProductEntityBuilder image3(String image3) { this.image3 = image3; return this; } public ProductEntityBuilder image4(String image4) { this.image4 = image4; return this; } public ProductEntityBuilder image5(String image5) { this.image5 = image5; return this; } // 其他Builder代码保留... } }
2. 修改Controller接口,支持多文件上传
更新接口参数为文件数组,添加数量校验(2-5张)和空文件校验:
@RestController public class ProductController { private ProductService productService; private ProductImageService productImageService; public ProductController(ProductService productService, ProductImageService productImageService) { this.productService = productService; this.productImageService = productImageService; } @PostMapping("/product/upload") public ResponseEntity<CustomPaymentResponse> uploadProduct( @RequestParam("productImages") MultipartFile[] files, @RequestPart("product") UploadProductDto uploadProductDto, HttpServletRequest request) throws Exception { // 校验图片数量范围 if (files == null || files.length < 2 || files.length > 5) { return ResponseEntity.badRequest().body(new CustomPaymentResponse(false, "商品图片数量必须在2-5张之间", null)); } // 校验无空文件 for (MultipartFile file : files) { if (file.isEmpty()) { return ResponseEntity.badRequest().body(new CustomPaymentResponse(false, "存在空的图片文件", null)); } } return productService.uploadProduct(uploadProductDto, files, request); } // 图片下载接口无需修改 @GetMapping("/download/{productImageId}") public ResponseEntity<Resource>downloadProductImage(@PathVariable("productImageId")String productImageId) throws Exception { ProductImageEntity imageEntity = productImageService.getProductImage(productImageId); return ResponseEntity.ok() .contentType(MediaType.parseMediaType(imageEntity.getFileType())) .header(HttpHeaders.CONTENT_DISPOSITION,"image; fileName=\""+imageEntity.getFileName() +"\"") .body(new ByteArrayResource(imageEntity.getData())); } }
3. 改造Service层方法
3.1 更新ProductService接口签名
将单文件参数改为文件数组:
public interface ProductService { // 其他方法保留... ResponseEntity<CustomPaymentResponse> uploadProduct(UploadProductDto uploadProductDto, MultipartFile[] files, HttpServletRequest request) throws Exception; }
3.2 实现多文件上传与路径存储
修改ProductServiceImpl的uploadProduct方法,批量处理图片上传,将路径对应存入商品实体的image1-imageN字段:
@Service public class ProductServiceImpl implements ProductService { // 原有注入保留... @Transactional(rollbackFor = Exception.class) @Override public ResponseEntity<CustomPaymentResponse> uploadProduct(UploadProductDto uploadProductDto, MultipartFile[] files, HttpServletRequest request) throws Exception { SellerEntity seller = sellerRepository.findByEmail(uploadProductDto.getSellerEMail()); if (Objects.isNull(seller)) { return ResponseEntity.badRequest().body(new CustomPaymentResponse(false, "卖家邮箱不存在", null)); } if (uploadProductDto.getPrice().intValue() <= 0) { return ResponseEntity.badRequest().body(new CustomPaymentResponse(false, "商品价格无效", null)); } categoryName(uploadProductDto.getCategory()); // 批量上传图片并收集下载路径 List<String> imageUrls = new ArrayList<>(); try { for (MultipartFile file : files) { ProductImageEntity imageEntity = productImageService.saveProductImage(file); if (Objects.isNull(imageEntity)) { throw new Exception("图片格式不被接受"); } imageUrls.add("download/" + imageEntity.getId()); } } catch (Exception e) { return ResponseEntity.badRequest().body(new CustomPaymentResponse(false, "图片上传失败:" + e.getMessage(), null)); } // 构建商品实体,根据图片数量设置对应路径字段 ProductEntity.ProductEntityBuilder productBuilder = ProductEntity.builder() .productName(uploadProductDto.getProductName()) .brand(uploadProductDto.getBrand()) .category(uploadProductDto.getCategory()) .description(uploadProductDto.getDescription()) .productOwner(seller) .price(uploadProductDto.getPrice()) .status("Pending Approval") .paymentStatus(PaymentStatus.PENDING.name()) .uploadedDate(new Date()) .approvedOrRejectedDate(null) .updatedDate(null); switch (imageUrls.size()) { case 2: productBuilder.image1(imageUrls.get(0)).image2(imageUrls.get(1)); break; case 3: productBuilder.image1(imageUrls.get(0)).image2(imageUrls.get(1)).image3(imageUrls.get(2)); break; case 4: productBuilder.image1(imageUrls.get(0)).image2(imageUrls.get(1)).image3(imageUrls.get(2)).image4(imageUrls.get(3)); break; case 5: productBuilder.image1(imageUrls.get(0)).image2(imageUrls.get(1)).image3(imageUrls.get(2)).image4(imageUrls.get(3)).image5(imageUrls.get(4)); break; default: return ResponseEntity.badRequest().body(new CustomPaymentResponse(false, "图片数量异常", null)); } ProductEntity product = productBuilder.build(); productRepository.save(product); return sellerPayStackService.initializePayment(product.getId()); } // 其他方法保留... }
3.3 可选:优化图片批量上传(性能提升)
在ProductImageService中添加批量保存方法,减少数据库交互次数:
// ProductImageService接口新增 List<ProductImageEntity> saveProductImages(List<MultipartFile> files) throws Exception; // ProductImageServiceImpl实现 @Override public List<ProductImageEntity> saveProductImages(List<MultipartFile> files) throws Exception { List<ProductImageEntity> imageEntities = new ArrayList<>(); for (MultipartFile file : files) { String fileName = StringUtils.cleanPath(file.getOriginalFilename()); if (fileName.contains("..")) { throw new DataNotAcceptableException("文件名包含无效字符"); } ProductImageEntity entity = new ProductImageEntity(file.getContentType(), fileName, file.getBytes()); imageEntities.add(entity); } return productImageRepository.saveAll(imageEntities); }
之后在ProductServiceImpl中调用该方法替换循环单个保存即可。
4. 数据库调整
确保ProductEntity对应的数据库表中新增image1到image5五个字符串类型字段(推荐VARCHAR(255))。
内容的提问来源于stack exchange,提问作者Ukeme Elijah
相关产品推荐
相关产品推荐

