使用 Spring Boot 和 EasyExcel 实现高效的 Excel 文件导入与图片处理

王疏蔬 2024-12-09 11:04:05编程技术
113

随着企业信息化程度的提高,数据管理和处理变得越来越重要。Excel 文件作为常用的数据存储和交换格式,其导入功能在许多业务场景中不可或缺。本文将介绍如何使用 Spring Boot 框架结合 EasyExcel 和 Apache POI 库,实现高效的数据导入和图片处理功能。通过这一方案,开发者可以轻松地将 Excel 文件中的数据导入系统,并处理其中的图片,从而提升数据处理的效率和准确性。

前言

项目中,要求批量导入 Excel 数据并读取其中图片,目前 EasyExcel 不支持读取图片,因此需要使用 Apache POI 进行导入。然而Apache POI 需要开发者手动管理行、列、单元格等对象,相对较为底层且繁琐。

使用 Spring Boot 和 EasyExcel 实现高效的 Excel 文件导入与图片处理

作者随即想到了一种方法,既能够使用 EasyExcel 的简便导入方式,又能够识别图片并进行处理。

相关依赖

        <!-- Apache POI -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.3</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.3</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.2.1</version>
        </dependency>

Java 代码展示

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.bi.my.vo.ExcelVo;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.result.R;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * 数据+图片导入
 *
 * @author wss
 */
@Slf4j
@RestController
@RequestMapping("/easy")
public class EasyExcelController {

    /**
     * 数据导入并识别图片
     *
     * @param file file
     * @return R<Object>
     * @author wss
     */
    @PostMapping("/import")
    public R<Object> dataImport(@RequestParam("file") MultipartFile file) {
        List<ExcelVo> excelVos = new ArrayList<>();
        //数据处理
        this.dataProcess(file, excelVos);
        //图像处理
        this.imageProcess(file, excelVos);
        //返回结果,这里也可以处理excelVos数据,如往mysql存储
        return R.success(excelVos);
    }

    /**
     * 图片处理
     */
    public void imageProcess(MultipartFile file, List<ExcelVo> excelVos) {
        try {
            XSSFWorkbook book = new XSSFWorkbook(file.getInputStream());
            //方式1 获取sheet数量,采用下标方式遍历读取每个工作表数据
            int sheetsNos = book.getNumberOfSheets();
            for (int sheetNo = 0; sheetNo < sheetsNos; sheetNo++) {
                Sheet sheet = book.getSheetAt(sheetNo);
                //...省略,内容为方式2
            }
            //方式2 获取sheet数量,直接遍历读取每个工作表数据
            for (Sheet sheet : book) {
                XSSFSheet xssSheet = (XSSFSheet) sheet;
                //获取工作表中绘图包
                XSSFDrawing drawing = xssSheet.getDrawingPatriarch();
                if (drawing == null) {
                    break;
                }
                //获取所有图像形状
                List<XSSFShape> shapes = drawing.getShapes();
                //遍历所有形状
                for (XSSFShape shape : shapes) {
                    //获取形状在工作表中的顶点位置信息(anchor锚点)
                    XSSFClientAnchor anchor = (XSSFClientAnchor) shape.getAnchor();
                    //图片形状在工作表中的位置, 所在行列起点和终点位置
                    short c1 = anchor.getCol1();
                    int r1 = anchor.getRow1();
                    String key = r1 + "行," + c1 + "列";
                    if (shape instanceof XSSFPicture) {
                        try {
                            XSSFPicture pic = (XSSFPicture) shape;
                            //形状获取对应的图片数据
                            XSSFPictureData picData = pic.getPictureData();
                            //保存图片到本地
                            byte[] data = picData.getData();
                            //TODO 这里上传文件至oss并生成链接,这里不做过多描述,有疑问请参照oss服务调用
                            String fileName = "https://oss.cn/" + DateUtil.today() + "/" + IdUtil.simpleUUID() + "/" + picData.suggestFileExtension();
                            //fileTemplate.putObject(properties.getBucketName(), fileName, new ByteArrayInputStream(data));
                            //TODO 放入excel集合,这里行数要减去1,获取图片是从表头开始(表头位置为0),获取excelVos是从数据开始(第一条数据位置为0)他们相差一个表头,所以要减去1才能对应
                            excelVos.get(r1 - 1).setPicture(fileName);
                        } catch (Exception e) {
                            log.error("asyncImportList XSSFClientAnchor key|{} error|{}", key, e.getMessage());
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("asyncImportList XSSFWorkbook error|{}", e.getMessage());
        }
    }

    /**
     * 数据处理
     */
    public void dataProcess(MultipartFile file, List<ExcelVo> excelVos) {
        // 这里默认读取第一个sheet
        try {
            EasyExcel.read(file.getInputStream(), ExcelVo.class, new ReadListener() {
                /**
                 * 单次缓存的数据量
                 */
                public static final int BATCH_COUNT = 100;
                /**
                 *临时存储
                 */
                private List<ExcelVo> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

                @Override
                public void invoke(Object object, AnalysisContext context) {
                    ExcelVo data = (ExcelVo) object;
                    cachedDataList.add(data);
                    if (cachedDataList.size() >= BATCH_COUNT) {
                        saveData();
                        // 存储完成清理 list
                        cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
                    }
                }

                @Override
                public void doAfterAllAnalysed(AnalysisContext context) {
                    saveData();
                }

                /**
                 * 加上存储数据库
                 */
                private void saveData() {
                    log.info("已获取数据|{}条", cachedDataList.size());
                    excelVos.addAll(cachedDataList);
                }
            }).sheet().doRead();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
import com.alibaba.excel.annotation.ExcelProperty;
import lombok.Data;

/**
 * 导入VO
 *
 * @author wss
 */
@Data
public class ExcelVo {
    @ExcelProperty("序号")
    private Integer ordinal;
    @ExcelProperty("标题")
    private String title;
    @ExcelProperty("图片")
    private String picture;
}

Excel 表格准备

excel表头需和实体字段相对应

使用 Spring Boot 和 EasyExcel 实现高效的 Excel 文件导入与图片处理

Postman 调用测试

打开图片链接发现与excel中图片一致,对应数据一致,读取图片成功!

使用 Spring Boot 和 EasyExcel 实现高效的 Excel 文件导入与图片处理

结尾

到这里,EasyExcel 导入带图片已完成!读取图片速度较慢,可以通过异步或其它方式优化处理,根据自己需求修改即可,这里就不进行说明了。

补充

项目中本人是通过@RequestExcel注解直接获取的excel数据,file专用于图片读取,即方法中不用再进行数据处理,代码更加简化。

这篇文章主要是用来处理读取图片的,这里就不再详细说明该注解了,感兴趣的小伙伴可以查阅一下相关资料。

    /**
     * 数据导入并识别图片
     *
     * @param file file
     * @return R<Object>
     * @author wss
     */
    @PostMapping("/import")
    public R<Object> dataImport(@RequestParam("file") MultipartFile file, @RequestExcel List<ExcelVo> excelVos) {
        //图像处理
        this.imageProcess(file, excelVos);
        //返回结果,这里也可以处理excelVos数据,如往库里存储
        return R.success(excelVos);
    }

总结

本文详细介绍了如何在 Spring Boot 项目中使用 EasyExcel 和 Apache POI 库实现 Excel 文件的导入和图片处理功能。通过EasyExcel库,我们可以高效地读取和处理 Excel 文件中的数据,而Apache POI则帮助我们提取和保存文件中的图片。整个过程通过一个简单的控制器接口实现,确保了系统的易用性和扩展性。此外,本文还提供了异常处理机制,确保在数据处理过程中系统的稳定性和可靠性。通过这些技术手段,开发者可以快速构建出高效、稳定的数据导入和图片处理系统,满足各种业务需求。

spring boot EasyExcel
THE END
蜜芽
故事不长,也不难讲,四字概括,毫无意义。

相关推荐