IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> EasyExcel读写文件 -> 正文阅读

[移动开发]EasyExcel读写文件

EasyExcel读写文件

1、写文件

1.1 定义实体类

@Data
public class DemoExportDto {
    @ExcelProperty("生成时间")
    private String createTime;
    @ExcelProperty("订单号")
    private String orderNo;
    @ExcelProperty("状态")
    private String status;
    @ExcelProperty("更新时间")
    private String updateTime;
    @ExcelProperty("地址")
    private String address;
}

1.2 获取表头样式

/**
     * 获取写表头的样式
     *
     * @return 返回值
     */
    public static WriteCellStyle getHeadWriteCellStyle() {
        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
        headWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        WriteFont writeFont = new WriteFont();
        writeFont.setColor(IndexedColors.BLACK.getIndex());
        writeFont.setBold(Boolean.TRUE);
        writeFont.setFontHeightInPoints((short) 15);
        headWriteCellStyle.setWriteFont(writeFont);
        return headWriteCellStyle;
    }

1.3 获取内容样式

/**
     * 获取写内容的样式
     *
     * @return 返回值
     */
    public static WriteCellStyle getContentWriteCellStyle() {
        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
        contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
        WriteFont writeFont = new WriteFont();
        writeFont.setColor(IndexedColors.BLACK.getIndex());
        writeFont.setBold(Boolean.FALSE);
        writeFont.setFontHeightInPoints((short) 12);
        contentWriteCellStyle.setWriteFont(writeFont);
        return contentWriteCellStyle;
    }

1.4 定义标注处理器

public class CellCommentWriteHandler implements CellWriteHandler {

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                 List<WriteCellData<?>> cellDataList, Cell cell, Head head,
                                 Integer relativeRowIndex, Boolean isHead) {
        addCellComment(cell);
    }

    /**
     * 给表头添加批注信息
     *
     * @param cell 单元格
     */
    private void addCellComment(Cell cell) {
        int columnIndex = cell.getColumnIndex();
        int rowIndex = cell.getRowIndex();
        if (rowIndex < 1) {
            Drawing<?> drawing = cell.getSheet().createDrawingPatriarch();
            Comment cellComment = drawing.createCellComment(new XSSFClientAnchor(0, 0, 0, 0, columnIndex, rowIndex, columnIndex + 2, rowIndex + 6));
            String value = cell.getStringCellValue();
            cellComment.setString(new XSSFRichTextString(value + "这是标注信息..."));
            cell.setCellComment(cellComment);
        }
    }

}

1.5 写文件

1.5.1 全量写入文件

public static void writeFileWithCustomStyle(String file, List<DemoExportDto> list) {
        WriteCellStyle headWriteCellStyle = ExportHandler.getHeadWriteCellStyle();
        WriteCellStyle contentWriteCellStyle = ExportHandler.getContentWriteCellStyle();

        EasyExcelFactory.write(file, DemoExportDto.class)
                // 指定表头
                .head(DemoExportDto.class)
                // 写入sheet的名称
                .sheet("默认sheet1名称")
                // 注册自定义写入Handler,分别为:自适应列宽、自定义样式、自定义标注处理器
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .registerWriteHandler(new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle))
                .registerWriteHandler(new CellCommentWriteHandler())
                // 开始写入数据
                .doWrite(list);
    }

1.5.2 分批写入文件

public static void writeFileWithCustomStyle2(String file) {
        ExcelWriter excelWriter = null;
        try {
            WriteCellStyle headWriteCellStyle = ExportHandler.getHeadWriteCellStyle();
            WriteCellStyle contentWriteCellStyle = ExportHandler.getContentWriteCellStyle();

            excelWriter = EasyExcelFactory.write(file, DemoExportDto.class)
                    // 注册自定义写入Handler,分别为:自适应列宽、自定义样式、自定义标注处理器
                    .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                    .registerWriteHandler(new HorizontalCellStyleStrategy(headWriteCellStyle, contentWriteCellStyle))
                    .registerWriteHandler(new CellCommentWriteHandler())
                    .build();

            // 这里注意:如果同一个sheet只要创建一次,持有excelWriter以及writeSheet对象引用即可分批次写入文件,最后在关闭流
            WriteSheet writeSheet = EasyExcel.writerSheet("sheet表格名称").build();
            for (int i = 0; i < 10; i++) {
                List<LrwMainExportDto> list = ExportHandler.formatDataList();
                excelWriter.write(list, writeSheet);
            }
        } finally {
            // 千万别忘记finish 会帮忙关闭流
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }

LongestMatchColumnWidthStyleStrategy:自适应列宽处理器,内置处理器

HorizontalCellStyleStrategy:单元格样式处理器,内置处理器

CellCommentWriteHandler:单元格标注处理器,自定义处理器

2、读文件

2.1 自定义分页处理器

public class PageReadListener<T> implements ReadListener<T> {
    /**
     * Single handle the amount of data
     */
    public final int batchCount;
    /**
     * Temporary storage of data
     */
    private List<T> cachedData;
    /**
     * consumer
     */
    private final Consumer<List<T>> consumer;

    public PageReadListener(Consumer<List<T>> consumer) {
        this.consumer = consumer;
        this.batchCount = 1000;
        cachedData = ListUtils.newArrayListWithExpectedSize(batchCount);
    }

    public PageReadListener(int batchCount, Consumer<List<T>> consumer) {
        this.consumer = consumer;
        this.batchCount = batchCount;
        cachedData = ListUtils.newArrayListWithExpectedSize(batchCount);
    }

    @Override
    public void invoke(T data, AnalysisContext context) {
        cachedData.add(data);
        if (cachedData.size() >= batchCount) {
            consumer.accept(cachedData);
            // 存储完成清理 list
            cachedData = ListUtils.newArrayListWithExpectedSize(batchCount);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        consumer.accept(cachedData);
    }

}

2.2 分页读取文件并重新写文件

private void doAnalyzeAndWrite(InputStream inputStream, ExcelWriter excelWriter, WriteSheet writeSheet) {
        AtomicInteger index = new AtomicInteger(1);
        AtomicReference<Integer> size = new AtomicReference<>(0);
        EasyExcel
                .read(inputStream, DemoExportDto.class, new PageReadListener<DemoExportDto>(1000, lines -> {
                    log.info("开始处理第 {} 页数据!共:{} 条!", index.getAndIncrement(), lines.size());
                    // 更近总记录数
                    size.updateAndGet(v -> v + lines.size());
                    // 判断任务运行状态,如果为false,则不在调用下游接口
                    if (Boolean.TRUE.equals(this.running)) {
                        // 批量处理该页的所有数据
                        this.doAnalysis(lines);
                    }
                    // 将处理结果追加写入文件中
                    excelWriter.write(lines, writeSheet);
                }))
                // 忽略空行
                .ignoreEmptyRow(Boolean.TRUE)
                // 读取第一个sheet页
                .sheet(0)
                // 表头行数
                .headRowNumber(1)
                // 自动Trim
                .autoTrim(Boolean.TRUE)
                .doReadSync();
        log.info("处理文件完成,共得处理数据:{}条", size.get());
    }

2.3 自定义读取文件监听器

自定义监听器,用来处理Excel文件,读取出来获取表头内容以及将数据表示为Map类型,key为表头,value为表格内容。

@Slf4j
public class ExcelListener extends AnalysisEventListener<Object> {

    private final Integer maxRow;
    private final Integer maxColumn;
    @Getter
    private final Map<Integer, String> tableHeadMap = new HashMap<>(32);
    @Getter
    private final List<Map<String, String>> content = new ArrayList<>(100);

    public ExcelListener(Integer maxRow) {
        this.maxRow = maxRow;
        this.maxColumn = 100;
    }

    /**
     * 新的构造器,传入解析Excel最大行数以及最大列数,如果不传,默认解析最大100列数据
     *
     * @param maxRow    最大行数
     * @param maxColumn 最大列数
     */
    public ExcelListener(Integer maxRow, Integer maxColumn) {
        this.maxRow = maxRow;
        this.maxColumn = maxColumn;
    }

    /**
     * 读取完一行数据后执行的方法
     *
     * @param data    一行数据
     * @param context 上下文
     */
    @Override
    @SuppressWarnings("unchecked")
    public void invoke(Object data, AnalysisContext context) {
        Integer line = null;
        Integer column = null;
        try {
            ReadRowHolder readRowHolder = context.readRowHolder();
            Integer rowIndex = readRowHolder.getRowIndex();
            line = rowIndex + 1;

            Map<Integer, Cell> cellMap = readRowHolder.getCellMap();
            Map<String, String> map = new HashMap<>(32);
            for (Map.Entry<Integer, Cell> entry : cellMap.entrySet()) {
                Integer index = entry.getKey();
                column = index + 1;
                if (column > maxColumn) {
                    String message = String.format("解析第%s行时,数据列共有%s列,超过最大限制%s,后续列不在解析", line, cellMap.size(), maxColumn);
                    log.warn(message);
                    break;
                }

                String title = tableHeadMap.get(index);
                Cell cell = entry.getValue();
                if (cell instanceof ReadCellData) {
                    ReadCellData<String> readCellData = (ReadCellData<String>) cell;
                    String value = this.getCellValue(line, column, title, readCellData);
                    // 对value值进行trim处理
                    if (StringUtils.isNotBlank(value)) {
                        value = value.trim();
                    }
                    map.put(title, value);
                } else {
                    log.info("其余字段类型不处理!单元格类型:{},当前行数:{}", cell.getClass(), line);
                }
            }
            content.add(map);
        } catch (Exception e) {
            String message = String.format("解析第%s行 第%s列 内容出错!错误信息为:%s", line, column, e.getMessage());
            log.error("解析Excel内容出错,{}", message, e);
            throw new BizException(message);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        log.info("读取Excel结束!共读取结果:{}行", content.size());
    }

    /**
     * 处理表头信息,第二行为表头中文数据,将表头数据添加到表头的Map中
     * Map.Key = 表头下标
     * Map.Value = 表头中文名称
     *
     * @param headMap 表头数据
     * @param context 上下文
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        Integer line = null;
        Integer column = null;
        try {
            ReadRowHolder readRowHolder = context.readRowHolder();
            Integer row = readRowHolder.getRowIndex();
            line = row + 1;
            if (log.isInfoEnabled()) {
                log.info("第 {} 行表头为:{}", line, JSON.toJSONString(headMap));
            }
            if (row == 1) {
                // 将表头中的特殊字符去掉
                for (Map.Entry<Integer, String> entry : headMap.entrySet()) {
                    Integer index = entry.getKey();
                    column = index + 1;
                    // 最大解析 maxColumn 的列,后续不解析
                    if (column > maxColumn) {
                        String message = String.format("解析第%s行时,表头列共有%s列,超过最大限制%s,后续表头不在解析", line, headMap.size(), maxColumn);
                        log.error(message);
                        break;
                    }

                    String title = entry.getValue();
                    tableHeadMap.put(index, this.formatString(title));
                }
            }
        } catch (Exception e) {
            String message = String.format("解析第%s行 第%s列 表头出错!错误信息为:%s", line, column, e.getMessage());
            log.error("解析Excel表头出错,{}", message, e);
            throw new BizException(message);
        }
    }

    /**
     * 处理批注等额外信息
     *
     * @param extra   extra information
     * @param context analysis context
     */
    @Override
    public void extra(CellExtra extra, AnalysisContext context) {
        log.info("读取到了一条额外信息:{}", JSON.toJSONString(extra));
        String key1 = "Sheet1!A1";
        String key2 = "Sheet2!A1";
        switch (extra.getType()) {
            case COMMENT:
                log.info("额外信息是批注,在rowIndex:{},columnIndex;{},内容是:{}", extra.getRowIndex(), extra.getColumnIndex(),
                        extra.getText());
                break;
            case HYPERLINK:
                if (key1.equals(extra.getText())) {
                    log.info("额外信息是超链接,在rowIndex:{},columnIndex;{},内容是:{}",
                            extra.getRowIndex(), extra.getColumnIndex(), extra.getText());
                } else if (key2.equals(extra.getText())) {
                    log.info("额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{},"
                                    + "内容是:{}",
                            extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                            extra.getLastColumnIndex(), extra.getText());
                } else {
                    throw new BizException("Unknown hyperlink!");
                }
                break;
            case MERGE:
                log.info("额外信息是超链接,而且覆盖了一个区间,在firstRowIndex:{},firstColumnIndex;{},lastRowIndex:{},lastColumnIndex:{}",
                        extra.getFirstRowIndex(), extra.getFirstColumnIndex(), extra.getLastRowIndex(),
                        extra.getLastColumnIndex());
                break;
            default:
        }
    }

    /**
     * 当处理数据时出现异常时的处理方式,是记录异常还是抛出异常由自己决定
     *
     * @param exception 异常
     * @param context   上下文
     * @throws Exception 抛出异常信息
     */
    @Override
    public void onException(Exception exception, AnalysisContext context) throws Exception {
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
            log.info("第 {} 行,第 {} 列解析异常,数据为:{}",
                    excelDataConvertException.getRowIndex(), excelDataConvertException.getColumnIndex(),
                    excelDataConvertException.getCellData().getData());
        }
        throw exception;
    }

    /**
     * 只处理指定行数的数据,防止数据量过大,当行数超过指定的最大行数时,返回false,即下面的数据不再处理
     *
     * @param context 上下文
     * @return 返回值,如果超过最大行数,则返回false,就不会继续执行了
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        ReadRowHolder readRowHolder = context.readRowHolder();
        Integer row = readRowHolder.getRowIndex();
        return row <= maxRow;
    }

    /**
     * 获取单元格内容
     *
     * @param rowIndex     当前行数
     * @param columnIndex  当前列
     * @param title        单元格表头
     * @param readCellData 单元格
     * @return 返回值
     */
    protected String getCellValue(Integer rowIndex, Integer columnIndex, String title, ReadCellData<String> readCellData) {
        String value = "";
        CellDataTypeEnum cellDataTypeEnum = readCellData.getType();

        if (CellDataTypeEnum.STRING.equals(cellDataTypeEnum)) {
            String object = readCellData.getStringValue();
            value = String.valueOf(object);
        } else if (CellDataTypeEnum.NUMBER.equals(cellDataTypeEnum)) {
            BigDecimal object = readCellData.getNumberValue();
            DataFormatData formatData = readCellData.getDataFormatData();
            Short formatDataIndex = formatData.getIndex();
            String format = formatData.getFormat();
            // 判断是否是日期格式
            if (DateUtils.isADateFormat(formatDataIndex, format)) {
                value = this.formatDate(object);
            } else {
                value = this.formatNumber(object);
            }
        } else if (CellDataTypeEnum.BOOLEAN.equals(cellDataTypeEnum)) {
            Boolean object = readCellData.getBooleanValue();
            value = String.valueOf(object);
        } else {
            log.info("字段类型为:{},当前行数为:{},当前列为:{},表头为:{},字段值为:{}",
                    cellDataTypeEnum, rowIndex, columnIndex, title, JSON.toJSONString(readCellData));
        }
        return value;
    }

    /**
     * 格式化数字,单元格读出来的数字有可能是科学计数法,所以此处需要转换
     *
     * @param object 入参单元格数字类型内容
     * @return 返回值
     */
    protected String formatNumber(Object object) {
        NumberFormat numberFormat = NumberFormat.getInstance();
        String result = numberFormat.format(object);
        // 这种方法对于自动加".0"的数字可直接解决
        // 但如果是科学计数法的数字就转换成了带逗号的,例如:12345678912345的科学计数法是1.23457E+13,
        // 经过这个格式化后就变成了字符串“12,345,678,912,345”,这也并不是想要的结果,所以要将逗号去掉
        String code = ",";
        if (result.contains(code)) {
            result = result.replace(code, "");
        }
        return result;
    }

    /**
     * 格式化日期,Excel中的日期记录的是一个double类型的数值,表示从1900年开始到现在所经过的天数,小数表示时间,所以需要将其转换为JAVA时间,然后格式化成字符串
     *
     * @param object 入参单元格日期类型内容
     * @return 返回值
     */
    protected String formatDate(Object object) {
        double excelDate = Double.parseDouble(String.valueOf(object));
        // 将Excel中保存的double类型的时间转换为字符串
        Date date = org.apache.poi.ss.usermodel.DateUtil
                .getJavaDate(excelDate, false);
        return DateUtil.dateToStr(date);
    }

    /**
     * 格式化字符串,将表头中的特殊字符去掉
     *
     * @param origin 原始字符串
     * @return 返回值
     */
    protected String formatString(String origin) {
        return origin.replace("*", "")
                .replace(",", "")
                .replace(",", "")
                ;
    }

}

2.4 读取文件

// 最多读取 MAX_ROW 行内容
        ExcelListener listener = new ExcelListener(MAX_ROW);
        List<DemoExportDto> list = EasyExcel.read(excelInputStream, DemoExportDto.class, listener)
                // 忽略空行
                .ignoreEmptyRow(Boolean.TRUE)
                // 读取第一个sheet页
                .sheet(0)
                // 表头行数
                .headRowNumber(2)
                // 自动Trim
                .autoTrim(Boolean.TRUE)
                .doReadSync();

        // 获取表头中文数据
        Map<Integer, String> tableHeadMap = listener.getTableHeadMap();
        // 获取Excel中的单据内容
        List<Map<String, String>> content = listener.getContent();

3、POM依赖

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>3.0.2</version>
        </dependency>
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-04-04 12:22:54  更:2022-04-04 12:26:50 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 20:33:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码