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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 05:22:01