poi导入封装成实体类,实现业务与获取数据代码分离。最后有导出封装

首先引入poi依赖

<poi-version>4.0.1</poi-version>


<!--POI-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>${poi-version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>${poi-version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>${poi-version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-excelant</artifactId>
            <version>${poi-version}</version>
        </dependency>

下面是我自己poi封装的结构

先说说普通导入吧:

这是转换单元格字符类型


/**
 * @program: ihrm_parent
 * @description 转换单元格字符类型
 * @author: 影耀子(YingYew)
 * @create: 2022-04-10 14:38
 **/
public class DataTypeHandle {

    public static Object getCellValue(Cell cell) {
        //1.获取到单元格的属性类型
        CellType cellType = cell.getCellType();
        //2.根据单元格数据类型获取数据
        Object value = null;
        switch (cellType) {
            case STRING:
                value = cell.getStringCellValue();
                break;
            case BOOLEAN:
                value = cell.getBooleanCellValue();
                break;
            case NUMERIC:
                if(DateUtil.isCellDateFormatted(cell)) {
                    //日期格式
                    value = cell.getDateCellValue();
                }else{
                    //数字
                    value = cell.getNumericCellValue();
                }
                break;
            case FORMULA: //公式
                value = cell.getCellFormula();
                break;
            default:
                break;
        }
        return value;
    }
}

基本excel导入的封装


/**
 * @program: ihrm_parent 基本excel导入,非百万
 * @description 为了做到统⼀的excel解析处理,进⾏通⽤的解析器封装,,放了满⾜⼀边解析数据⼀边存库的需要,通过
 * 接⼝实现
 * ExcelHandler
 * Consumer
 * 回调操作,保证解析到⼀定数据量后就可以进⾏后续操作。
 * @author: 影耀子(YingYew)
 * @create: 2022-06-04 14:34
 **/
public class ExcelHandler<T> {

    /**
     * 批量处理梳理
     */
    public Integer batchNum;

    /**
     * 开始解析的行号,第一行时,为0
     */
    public Integer startRow;

    /**
     * 封装的entity对象
     */
    public Class<?> entity;
    /**
     * 临时解析对象的构造器
     */
    public Constructor<?> constructor;

    /**
     * 传入解析数据的service对象
     */
    public Consumer<List<T>> uploadService;
    /**
     * 接收解析对象值
     */
    public List<T> list = new ArrayList<>();

    public ExcelHandler(Consumer<List<T>> uploadService, Class<?> entity,Integer batchNum, Integer startRow) {
        this.batchNum = batchNum == null?1000:batchNum;
        this.startRow = startRow;
        this.entity = entity;
        this.uploadService = uploadService;
    }

    /**
     * @param inputStream 数据流
     * @throws Exception
     */
    public void handleData(InputStream inputStream) throws Exception {
        // 创建构造器
        constructor = entity.getDeclaredConstructor(List.class);
        // 创建表格对象
        XSSFWorkbook workbook = new XSSFWorkbook(inputStream);
        // 有多少个sheet
        int sheets = workbook.getNumberOfSheets();
        for (int i = 0; i < sheets; i++) {
            Sheet sheet = workbook.getSheetAt(i);
            // 获取多少行
            int rows = sheet.getPhysicalNumberOfRows();
            // 第0为表头,第一行开始解析数据
            for (int j = 0; j < rows; j++){
                parseCellData(sheet,j);
            }
        }
        // 所有数据解析完成后
        saveData(list);
    }

    /**
     * 回调保存数据
     * @param list
     */
    private void saveData(List<T> list) {
        if (list.size() > 0) {
            uploadService.accept(list);
            list.clear();
        }
    }

    /**
     * 获取表格数据
     * @param sheet
     * @param currentRow
     */
    private void parseCellData(Sheet sheet, int currentRow) throws Exception {
        // 获取第行号
        Row row = sheet.getRow(currentRow);
        // 解析正文数据
        List<Object> values = new ArrayList<>();
        for (int i = 0; i < row.getLastCellNum(); i++) {
            Cell cell = row.getCell(i);
            Object value = DataTypeHandle.getCellValue(cell);
            values.add(value);
        }
        endRow(values,currentRow);
    }

    /**
     * 一行解析完成后
     * @param values
     * @param currentRow
     */
    private void endRow(List<Object> values, int currentRow) throws Exception {
        if (currentRow < startRow){
            System.out.println("解析头部数据:"+values);
        }else {
            System.out.println("解析表格数据:"+values);
            T data = (T) constructor.newInstance(values);
            list.add(data);
        }

        if (list.size() >= batchNum){
            // 回调接口,处理数据
            saveData(list);
        }
    }

}

这是实体类的转换模板,基本字段都比较全面


/**
 * @program: ihrm_parent
 * @description 实体类转换模板,若用的是百万数据导入的话,全部都是字符串,没有进行类型转换
 * @author: 影耀子(YingYew)
 * @create: 2022-06-04 15:30
 **/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class EntityMB {
    private Integer currentRow;
    private String name;
    private Integer agx;
    private String gender;
    private String address;
    private Double height;
    private Date date;
    private LocalDateTime date1;

    // 进⾏解析后的类型转换
    public EntityMB(List<Object> values){
        
        this.name = values.get(0)==null?"": values.get(0).toString();
        this.agx = values.get(1)==null?0:new Double(values.get(1).toString()).intValue();
        this.gender = values.get(2)==null?"": values.get(2).toString();
        this.address = values.get(3)==null?"": values.get(3).toString();
        this.date = (Date) (values.get(4));
        this.date1 = ((Date) (values.get(4))).toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
    }

    // 百万数据解析后全部都是字符串,需要在这里手动转换
    /*public EntityMB(List<String> values){    
        // 百万数据解析后第一个数据是当前行号
        this.currentRow = values.get(0)==null?0:new Double(values.get(0)).intValue();
        this.name = values.get(1)==null?"": values.get(1);
        this.agx = values.get(2)==null?0:new Double(values.get(2)).intValue();
        this.gender = values.get(3)==null?"": values.get(3);
        this.address = values.get(4)==null?"": values.get(4);
        this.date = DateUtil.parse(values.get(5));
        this.date1 = Convert.toLocalDateTime(values.get(5));
    }*/
}

使用封装的代码


/**
     * 导入用户
     * @return
     */
    @PostMapping("import")
    public R importUser1(@RequestParam(name = "file") MultipartFile file) throws Exception{

        // 使用全局导入
        Consumer<List<EntityMB>> consumer = this::globalImport;

        new ExcelHandler<>(consumer,EntityMB.class,null, 1 )
                .handleData(file.getInputStream());

        return R.FAIL();
    }


    private void globalImport(List<EntityMB> entityMBS) {
        System.out.println("导入的数据为"+entityMBS);

    }

接下来就是百万数据导入代码


/**
 * @program: ihrm_parent
 * @description 百万数据导出
 * @author: 影耀子(YingYew)
 * @create: 2022-06-04 16:52
 **/
public class XssfSheetHandler {
    /**
     *
     * @param inputStream 数据流
     * @param consumer 自定义回调
     * @param entityClass 解析数据实体
     * @param batchNum 批处理数量
     * @param startRow excel解析正文行号
     * @throws Exception
     */
    public static void handlerData(InputStream inputStream, Consumer consumer,Class<?> entityClass,
                                   Integer batchNum,Integer startRow) throws Exception {
        //1.根据excel报表获取OPCPackage
        OPCPackage opcPackage = OPCPackage.open(inputStream);
        //2.创建XSSFReader
        XSSFReader reader = new XSSFReader(opcPackage);
        //3.获取SharedStringTable对象
        SharedStringsTable table = reader.getSharedStringsTable();
        //4.获取styleTable对象
        StylesTable stylesTable = reader.getStylesTable();
        //5.创建Sax的xmlReader对象
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        //6.注册事件处理器
        XSSFSheetXMLHandler xmlHandler = new XSSFSheetXMLHandler(stylesTable,table
                ,new SheetHandler(batchNum,startRow,entityClass,consumer),false);
        xmlReader.setContentHandler(xmlHandler);
        //7.逐行读取
        XSSFReader.SheetIterator sheetIterator = (XSSFReader.SheetIterator) reader.getSheetsData();
        while (sheetIterator.hasNext()) {
            InputStream stream = sheetIterator.next(); //每一个sheet的流数据
            InputSource is = new InputSource(stream);
            xmlReader.parse(is);
        }
    }

}

然后就是自定义事件处理器


/**
 * 自定义的事件处理器
 *  处理每一行数据读取
 *      实现接口
 * @author 影耀子
 */
public class SheetHandler<T> implements XSSFSheetXMLHandler.SheetContentsHandler {

    /**
     * 批量处理梳理
     */
    public Integer batchNum;

    /**
     * 开始解析的行号,第一行时,为0
     */
    public Integer startRow;

    /**
     * 封装的实体类
     */
    public Class<?> entity;

    /**
     * 当前解析的行号
     */
    private Integer currentRow;

    /**
     * 临时解析对象的构造器
     */
    public Constructor<?> constructor;

    /**
     * 传入解析数据的service对象
     */
    public Consumer<List<T>> uploadService;
    /**
     * 接收解析对象值
     */
    public List<T> list = new ArrayList<>();
    /**
     * 解析头部数据
     */
    public List<String> headers = new ArrayList<>();
    /**
     * 解析单元格
     */
    public List<String> valuesList = new ArrayList<>();

    /**
     * 解析的行号(第几个单元格),默认0为第一列
     */
    public Integer cellNum = 0;

    /**
     * 存储列名与列名下标映射
     */
    public Map<Integer, String> map = new HashMap<>();

    public SheetHandler(Integer batchNum, Integer startRow, Class<?> entity, Consumer<List<T>> uploadService) throws Exception {
        this.constructor = entity.getDeclaredConstructor(List.class);
        this.batchNum = batchNum == null?1000:batchNum;
        this.startRow = startRow;
        this.entity = entity;
        this.uploadService = uploadService;
    }

    /**
     * 当开始解析某一行的时候触发
     *      i:行索引
     */
    @Override
    public void startRow(int i) {
        //实例化对象
        currentRow = i;
        if (currentRow < startRow){
            // 表头数据不添加当前行
            return;
        }
        // 给此行数据记录当前行
        valuesList.add(String.valueOf(currentRow + 1));
    }

    /**
     * 当结束解析某一行的时候触发
     *      i:行索引
     */
    @Override
    public void endRow(int i) {
        try {
            cellNum = 0;
            if (headers.size() > 0){
                System.out.println("解析的头部数据"+headers);
            }
            if (valuesList.size() > 0){
                // 兼容excel表格每行的最后列为null的错误
                int valueListSize = map.size();
                // 之所以用<=,是因为前面有记录它的当前行
                while (valuesList.size() <= valueListSize){
                    valuesList.add(null);
                }
                System.out.println("解析的表格数据"+valuesList);
                T data = (T) constructor.newInstance(valuesList);
                list.add(data);
            }
            if (list.size() >=batchNum){
                // 回调接口,处理数据
                saveData(list);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            headers.clear();
            valuesList.clear();
        }
    }

    /**
     * 对行中的每一个表格进行处理
     *      cellReference: 单元格名称
     *      value:数据
     *      xssfComment:批注
     */
    @Override
    public void cell(String cellReference, String value, XSSFComment xssfComment) {
        try {
            // 列名
            String cellName = cellReference.replace(String.valueOf(currentRow+1), "");
            if (currentRow < startRow){
                map.put(cellNum,cellName);
                // 加载表头数据
                headers.add(value);
            }else {
                while (!cellName.equals(map.get(cellNum))){
                    valuesList.add(null);
                    cellNum++;
                }
                valuesList.add(value);
            }
            cellNum++;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析完sheet,多个sheet回调多次
     */
    @Override
    public void endSheet() {
        // 解析完成
        saveData(list);
    }

    /**
     * 回调保存数据
     * @param list
     */
    private void saveData(List<T> list) {
        if (list.size() > 0) {
            uploadService.accept(list);
            list.clear();
        }
    }
}

调用百万数据导入的代码


/**
     * 导入用户
     * @return
     */
    @PostMapping("import")
    public R importUser1(@RequestParam(name = "file") MultipartFile file) throws Exception{

        // 百万数据全局导入test
        Consumer<List<EntityMB>> consumer = this::globalImport;
        XssfSheetHandler.handlerData(file.getInputStream(),consumer,EntityMB.class,null,1);

        return R.FAIL();
    }


    private void globalImport(List<EntityMB> entityMBS) {
        System.out.println("导入的数据为"+entityMBS);

    }

接下来就是poi的导出封装,借用的hutool工具类

引入依赖<hutool-all>5.8.3</hutool-all>


<!-- hutool工具类,几乎包含所以通用方法 -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>${hutool-all}</version>
        </dependency>

public class Excel {
    /**
     * 导出excel
     * @param data 数据
     * @param response 返回信息
     * @param returnName 文件名
     */
    public void export(List<?> data, HttpServletResponse response, String returnName) {
        BigExcelWriter writer= ExcelUtil.getBigWriter();
        writer.write(data,true);
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        writer.flush(os);
        writer.close();

        new DownloadUtils().download(os,response,returnName);
    }

}

/**
 * @author 影耀子YingYew
 * @Description: 文件下载工具类
 * @Package: com.lookvin.common.util.download
 * @ClassName: DownloadUtils
 * @date 2022/7/27 9:46
 */
public class DownloadUtils {

    /**
     * 下载文件工具类
     * @param byteArrayOutputStream
     * @param response
     * @param returnName
     * @throws IOException
     */
    public void download(ByteArrayOutputStream byteArrayOutputStream, HttpServletResponse response, String returnName) {

        try {
            response.setContentType("application/octet-stream");
            returnName = response.encodeURL(new String(returnName.getBytes(),"iso8859-1"));            //保存的文件名,必须和页面编码一致,否则乱码
            response.addHeader("content-disposition","attachment;filename=" + returnName);
            response.setContentLength(byteArrayOutputStream.size());
            ServletOutputStream outputstream = response.getOutputStream();
            byteArrayOutputStream.writeTo(outputstream);                    //写到输出流
            outputstream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
自己封装excel导出/导入,可以根据注解来导出excel.本项目一共有13个类,里面还包含了一个反射工具,一个编码工具,10分值了。下面是测试代码 public class Test { public static void main(String[] arg) throws FileNotFoundException, IOException{ testBean(); testMap(); } public static void testBean() throws FileNotFoundException, IOException{ List l = new ArrayList(); for(int i=0;i<100;i++){ l.add(new MyBean()); } //很轻松,只需要二句话就能导出excel BeanExport be = ExportExcel.BeanExport(MyBean.class); be.createBeanSheet("1月份", "1月份人员信息").addData(l); be.createBeanSheet("2月份","2月份人员信息").addData(l); be.writeFile("E:/test/bean人员信息8.xlsx"); } //如果不想用注解,还能根据MAP导出. public static void testMap () throws FileNotFoundException, IOException{ List l = new ArrayList(); l.add(new MapHeader("姓名","name",5000)); l.add(new MapHeader("年龄","age",4000)); l.add(new MapHeader("生日","birthdate",3000)); l.add(new MapHeader("地址","address",5000)); l.add(new MapHeader("双精度","d",4000)); l.add(new MapHeader("float","f",6000)); List<Map> lm = new ArrayList<Map>(); for(int i=0;i<100;i++){ Map map = new HashMap(); map.put("name","闪电球"); map.put("age",100); map.put("birthdate",new Date()); map.put("address","北京市广东省AAA号123楼!"); map.put("d",22.222d); map.put("f",295.22f); lm.add(map); } MapExport me = ExportExcel.mapExport(l); me.createMapSheel("1月份","广东省人员信息").addData(lm); me.createMapSheel("2月份", "北京市人员信息").addData(lm); me.writeFile("E:/test/map人员信息9.xlsx"); } }
要将Excel中的数据导入实体类中,你可以使用Apache POI库来处理Excel文件。下面是一个简单的示例代码,演示如何使用POI导入Excel数据到实体类中。 ```java import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import java.io.FileInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ExcelImporter { public List<YourEntityClass> importDataFromExcel(String filePath) throws IOException { List<YourEntityClass> entities = new ArrayList<>(); FileInputStream fis = new FileInputStream(filePath); Workbook workbook = new XSSFWorkbook(fis); Sheet sheet = workbook.getSheetAt(0); // 获取第一个Sheet Iterator<Row> rowIterator = sheet.iterator(); while (rowIterator.hasNext()) { Row row = rowIterator.next(); Iterator<Cell> cellIterator = row.cellIterator(); YourEntityClass entity = new YourEntityClass(); // 根据Excel列的顺序,获取对应的单元格数据并设置到实体类中 Cell cell1 = cellIterator.next(); entity.setField1(cell1.getStringCellValue()); Cell cell2 = cellIterator.next(); entity.setField2(cell2.getNumericCellValue()); // ... 继续设置其他字段 entities.add(entity); } workbook.close(); fis.close(); return entities; } } ``` 在这个示例中,你需要将`YourEntityClass`替换为你自己的实体类名字,并根据Excel的列顺序逐个获取对应单元格的数据,然后设置到实体类的字段中。你可以根据需要,使用不同的`getCellType()`方法来获取不同类型的单元格值。 请注意,此代码仅适用于`.xlsx`格式的Excel文件。如果你要导入`.xls`格式的文件,你需要使用`HSSFWorkbook`替代`XSSFWorkbook`。 同时,记得处理可能出现的异常情况,如文件不存在或Excel格式不正确等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值