EasyExcel 导出 excel 后无法实现日期筛选与数值类型合计问题与解决

文章目录

  • 问题现象
    • 1、对于日期类型
      • 1.1 excel对日期类型筛选后正常现象
      • 1.2 easyexcel导出后对日期类型筛选后异常现象
    • 2、对于金额类型
      • 2.1 程序解决方案
      • 2.2 实现类 OrderCellHandler.java
    • 3、导出超过500条与导出后显示文件损毁问题
      • 3.1 index 索引定向指定,取消 index 指向方式
      • 3.2 数据量超过500时,刷新缓存表头会丢失问题处理
      • 3.3 附最新全部代码 OrderCellHandler.java
    • 4、抽出公共handler避免一个功能写一个handler
      • 4.1 调用端组装需要转换的参数 map 传入
      • 4.2 工具类 EasyExcelUtil.writeCommonExcel()取map并注册公共handler
      • 4.3 公共handler代码(含对列宽的处理)

问题现象

1、对于日期类型

excel做筛选后应该支持按年、月、日筛选,而easyexcel 按注解 @DateTimeFormat(“yyyy-MM-dd”) 实现后虽然呈现格式上没问题,但是单元格格式还是存在问题导致筛选后不正常,具体如图效果:

1.1 excel对日期类型筛选后正常现象

EasyExcel 导出 excel 后无法实现日期筛选与数值类型合计问题与解决_第1张图片

1.2 easyexcel导出后对日期类型筛选后异常现象

EasyExcel 导出 excel 后无法实现日期筛选与数值类型合计问题与解决_第2张图片
补充现象:
easyexcel 导出后默认是未双击效果,上面得筛选结果就是未双击这版格式
如果鼠标左键双击后,excel就会自动转换正常得日期格式,就能筛选了。
本次咱们通过程序解决这个问题。

PS:即使在excel手动设置单元格格式也不生效,所以只能程序解决!

EasyExcel 导出 excel 后无法实现日期筛选与数值类型合计问题与解决_第3张图片

2、对于金额类型

呈现效果类似上面日期问题,也是导出默认得格式不是真正得数值类型,做合计得时候无法合计。

PS:即使在excel手动设置单元格格式也不生效,所以只能程序解决!

2.1 程序解决方案

实现一个注册一个自定义handler,implements CellWriteHandler 即
.registerWriteHandler(new OrderCellHandler())

	/**
	 * 导出 Excel :一个 sheet,带表头.
	 *
	 * @param response  HttpServletResponse
	 * @param data      数据 list,每个元素为一个 BaseRowModel
	 * @param fileName  导出的文件名
	 * @param sheetName 导入文件的 sheet 名
	 * @param clazz     映射实体类,Excel 模型
	 * @throws Exception 异常
	 */
	public static <T> void writeExcelWithColumnNames(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> clazz, List<String> columnNames) throws Exception {
		// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
		try {
			response.setContentType("application/vnd.ms-excel");
			response.setCharacterEncoding("utf-8");
			response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
			// 这里需要设置不关闭流
			EasyExcelFactory.write(response.getOutputStream(), clazz)
					.registerWriteHandler(new OrderCellHandler())
					.includeColumnFiledNames(columnNames)
					.autoCloseStream(Boolean.FALSE)
					.sheet(sheetName)
					.doWrite(data);
		} catch (Exception e) {
			String message = "下载文件失败" + e.getMessage();
			log.error(message, e);
		}
	}

2.2 实现类 OrderCellHandler.java

package com.fawm.base.config.easy.excel.write.handler;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.fawm.base.utils.CommonDateUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;

import java.text.ParseException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 订单导出处理日期格式与金额类
 *
 * @author liumingxing
 */
public class OrderCellHandler implements CellWriteHandler {
    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        boolean columnTwoFlag = cell.getColumnIndex() == 2;
        boolean otherDateFlag = cell.getColumnIndex() >= 65 && cell.getColumnIndex() <= 71;
        boolean dateFlag = columnTwoFlag || otherDateFlag;
        int[] moneyColumnIndex = {8, 9, 10, 11, 51, 52, 53, 55, 57, 59, 61, 62, 64};
        int matchMoney = Arrays.stream(moneyColumnIndex).filter(item -> item == cell.getColumnIndex()).findAny().orElse(-1);
        boolean moneyColumnFlag = matchMoney != -1;
        boolean finalFlag = dateFlag || moneyColumnFlag;
        if (!isHead && finalFlag) {
            Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
            CellStyle cellStyle = workbook.createCellStyle();
            String stringCellValue = cell.getStringCellValue();
            try {
                CreationHelper createHelper = workbook.getCreationHelper();
                if (StringUtils.isNotBlank(stringCellValue) && dateFlag) {
                    String[] patterns = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd"};
                    Date date = CommonDateUtils.parseDate(stringCellValue, patterns);
                    if (columnTwoFlag) {
                        //订单生成日期
                        cellStyle.setDataFormat(createHelper.createDataFormat().getFormat("m/d/yy h:mm:ss"));
                    } else {
                        //其他日期列
                        cellStyle.setDataFormat(createHelper.createDataFormat().getFormat("m/d/yy"));
                    }
                    cell.setCellStyle(cellStyle);
                    cell.setCellValue(date);
                } else if (StringUtils.isNotBlank(stringCellValue) && moneyColumnFlag) {
                    //其他金额列
                    Float money = Float.parseFloat(stringCellValue);
                    cellStyle.setDataFormat(createHelper.createDataFormat().getFormat("#,##0.00"));
                    cell.setCellStyle(cellStyle);
                    cell.setCellValue(money);
                }
            } catch (ParseException e) {
                e.printStackTrace();
            }

        }
    }
}

3、导出超过500条与导出后显示文件损毁问题

当实现上面的功能后,经过测试验证,偶发的出现下面这个问题,显示文件损毁而不能打开。后来经过排查主要是两方面原因:

  • 一是对日期、金额类型的列做转换时是按照列 index 索引指定处理转换的。而在我这个项目中,每个用户角色列头权限不一样,所以固定指 index 就会出现把非日期的列转为日期类型,非数值的列转为数值类型,进而导致 excel 格式损坏;
  • 二是此方法实现转换时,数据量超过500时,会会自动 flush 刷新缓存,表头会丢失,进而报错。
    下面针对这两种情况进行处理(最后付全部代码,先单独解释)
    EasyExcel 导出 excel 后无法实现日期筛选与数值类型合计问题与解决_第4张图片

3.1 index 索引定向指定,取消 index 指向方式

很简单,通过列名判断,而不是 index 判断(不是最好的方法,但是可以解决诉求了而且不会出问题)

/**
     * 需要处理日期和金额的列名
     */

    String[] moneyColumns = {"保证金", "市场指导价", "员工购车价", "员工支付价", "开发票金额", "销售方案总金额", "购置税", "交强险", "车船税",
            "商业险", "精品", "汽油费", "其他费用"};
    /**
     * 需要处理的日期+时间列名
     */
    String[] dateTimeColumns = {"订单生成时间"};
    /**
     * 需要出列的日期列名
     */
    String[] dateColumns = {"订单日期", "保证金到账日期", "首付款/全款到账时间", "尾款到账时间", "开票日期", "初次配车日期", "实际出库日期"};
    
    //是否是日期加时间
	 boolean columnTwoFlag = StringUtils.isNotBlank(Arrays.stream(dateTimeColumns).filter(item -> item.equals(columnName)).findAny().orElse(""));
	 //是否是日期
	 boolean otherDateFlag = StringUtils.isNotBlank(Arrays.stream(dateColumns).filter(item -> item.equals(columnName)).findAny().orElse(""));
	 boolean dateFlag = columnTwoFlag || otherDateFlag;
	 //是否是金额
	 boolean moneyColumnFlag = StringUtils.isNotBlank(Arrays.stream(moneyColumns).filter(item -> item.equals(columnName)).findAny().orElse(""));
	 boolean finalFlag = dateFlag || moneyColumnFlag;

3.2 数据量超过500时,刷新缓存表头会丢失问题处理

方案: 先缓存一下所有列头

    /**
     * 列名存储列表
     * 数据量超过500时,会刷新缓存,表头会丢失,所以要存一下,以便查找
     */
    private static final List<String> COLUMNS = new ArrayList<>();
    /**
     * 初始化参数
     */
    public OrderCellHandler() {
        COLUMNS.clear();
        dateTimeStyle = null;
        dateStyle = null;
        moneyStyle = null;
    }
		
    if (isHead != null && isHead) {
        //获取表头,存储起来
        COLUMNS.add(cell.getStringCellValue());
    } else if (isHead != null) {
        // 结合第一步定义的需要处理的列名 moneyColumns、dateTimeColumns、dateColumns 动态判断
        String columnName = COLUMNS.get(cell.getColumnIndex());
    }

3.3 附最新全部代码 OrderCellHandler.java

package com.fawm.base.config.easy.excel.write.handler;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.fawm.base.utils.CommonDateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.*;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 订单导出处理日期格式与金额类
 *
 * @author liumingxing
 */
@Slf4j
public class OrderCellHandler implements CellWriteHandler {
    /**
     * 需要处理日期和金额的列名
     */

    String[] moneyColumns = {"保证金", "市场指导价", "员工购车价", "员工支付价", "开发票金额", "销售方案总金额", "购置税", "交强险", "车船税",
            "商业险", "精品", "汽油费", "其他费用"};
    /**
     * 需要处理的日期+时间列名
     */
    String[] dateTimeColumns = {"订单生成时间"};
    /**
     * 需要出列的日期列名
     */
    String[] dateColumns = {"订单日期", "保证金到账日期", "首付款/全款到账时间", "尾款到账时间", "开票日期", "初次配车日期", "实际出库日期"};
    /**
     * 列名存储列表
     * 数据量超过500时,会刷新缓存,表头会丢失,所以要存一下,以便查找
     */
    private static final List<String> COLUMNS = new ArrayList<>();

    /**
     * 时间+日期样式存储
     * 一个excel的sheet页,最多可以创建64000个样式,频繁创建会倒是程序报错
     */
    private CellStyle dateTimeStyle;
    /**
     * 时间样式存储
     * 一个excel的sheet页,最多可以创建64000个样式,频繁创建会倒是程序报错
     */
    private CellStyle dateStyle;
    /**
     * 金额样式存储
     * 一个excel的sheet页,最多可以创建64000个样式,频繁创建会倒是程序报错
     */
    private CellStyle moneyStyle;

    /**
     * 初始化参数
     */
    public OrderCellHandler() {
        COLUMNS.clear();
        dateTimeStyle = null;
        dateStyle = null;
        moneyStyle = null;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        //获取表头,存储起来
        if (isHead != null && isHead) {
            COLUMNS.add(cell.getStringCellValue());
        } else if (isHead != null) {
            String columnName = COLUMNS.get(cell.getColumnIndex());
            //是否是日期加时间
            boolean columnTwoFlag = StringUtils.isNotBlank(Arrays.stream(dateTimeColumns).filter(item -> item.equals(columnName)).findAny().orElse(""));
            //是否是日期
            boolean otherDateFlag = StringUtils.isNotBlank(Arrays.stream(dateColumns).filter(item -> item.equals(columnName)).findAny().orElse(""));
            boolean dateFlag = columnTwoFlag || otherDateFlag;
            //是否是金额
            boolean moneyColumnFlag = StringUtils.isNotBlank(Arrays.stream(moneyColumns).filter(item -> item.equals(columnName)).findAny().orElse(""));
            boolean finalFlag = dateFlag || moneyColumnFlag;
            if (finalFlag) {
                Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
                String stringCellValue = cell.getStringCellValue();
                try {
                    CreationHelper createHelper = workbook.getCreationHelper();
                    if (StringUtils.isNotBlank(stringCellValue) && dateFlag) {
                        String[] patterns = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd"};
                        Date date = DateUtils.parseDate(stringCellValue, patterns);
                        if (columnTwoFlag) {
                            //订单生成日期
                            if (dateTimeStyle == null) {
                                dateTimeStyle = workbook.createCellStyle();
                            }
                            dateTimeStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy/mm/dd hh:mm:ss"));
                            cell.setCellStyle(dateTimeStyle);
                        } else {
                            //其他日期列
                            if (dateStyle == null) {
                                dateStyle = workbook.createCellStyle();
                            }
                            dateStyle.setDataFormat(createHelper.createDataFormat().getFormat("yyyy/mm/dd"));
                            cell.setCellStyle(dateStyle);
                        }

                        cell.setCellValue(date);
                    } else if (StringUtils.isNotBlank(stringCellValue) && moneyColumnFlag) {
                        //其他金额列
                        double money = Double.parseDouble(stringCellValue);
                        if (moneyStyle == null) {
                            moneyStyle = workbook.createCellStyle();
                        }
                        moneyStyle.setDataFormat(createHelper.createDataFormat().getFormat("0.00"));
                        cell.setCellStyle(moneyStyle);
                        cell.setCellValue(money);
                    }
                } catch (ParseException e) {
                    log.info("导出订单信息时出现错误:值:{},行:{},列:{}", stringCellValue, cell.getRow().getRowNum(), cell.getColumnIndex());
                    cell.setCellValue("");
                }
            }
        }
    }
}

4、抽出公共handler避免一个功能写一个handler

前面写的方案局限性在于一个handler只能给一个导出功能使用,如果系统中存在多个业务模块有导出功能,就得写多个handler,很不方便,这里我把handler抽象出来,调用端仅需要对需要处理的字段传入即可。

另外对数值类型转换报错原因补充:只有dto中定义的数值字段为string类型时,excel导出才会有格式问题,如果dto中定义的字段为double或integer则不需要转换,easyexcel会按正常格式导出。所以大家传入需要转换的列名时需要判断一下dto里的类型是否是string类型(我程序中也做兼容了,不过最好还是人为判断一下,不然后台一直在吞异常)

4.1 调用端组装需要转换的参数 map 传入

    /**
     * 导出发车明细列表
     *
     * @param response
     * @param query
     */
    @ApiOperation("导出发车明细列表")
    @GetMapping(path = "export/departDetail")
    public void exportDepartDetail(HttpServletResponse response, StatisticalQuery query) {
        List<DepartDetailStatisticsDto> departDetailList = planBillboardService
                .departDetail(query, null);
        try {
            /**
             * dateTimeColumns:将需要转换单元格格式的日期、数值类型传入 handler(仅需要对导出的 excel 日期筛选以及数值合计有问题的列传入即可,没问题的列可以不用传入)
             * dateColumns:此功能导出的都待时间,没有导年月日的,不用处理
             * numberColumns:此功能 dto 中定义的数值类型都是 double ,不是string, 所以导出后正常不用特人数处理
             */
            Map<String, Object> map = new HashMap<>(3);
            map.put("dateTimeColumns", new String[]{"入库日期","倒运日期","PDI日期","交钥匙日期","交合格证日期","实车出库日期"});
            map.put("dateColumns", new String[]{});
            map.put("numberColumns", new String[]{});

            EasyExcelUtil.writeCommonExcel(response, "" + System.currentTimeMillis(), CommonConst.SHEET,
                    departDetailList, DepartDetailStatisticsDto.class, map);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }   

4.2 工具类 EasyExcelUtil.writeCommonExcel()取map并注册公共handler

.registerWriteHandler(new CommonExcelCellHandler(map))

  • 1、传入dto calzz 方式
    /**
     * 导出 Excel :一个 sheet,带日期、数值类型单元格转换.
     * @param response
     * @param fileName
     * @param sheetName
     * @param data
     * @param clazz
     * @param map
     * @param 
     * @throws Exception
     */
    public static <T> void writeCommonExcel(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> clazz,
            Map<String, Object> map) throws Exception {
        // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");

            // 没有传入特殊处理的列,避免 handler 初始化报错
            if (map == null || map.size() == 0){
                map.put("dateTimeColumns", new String[0]);
                map.put("dateColumns", new String[0]);
                map.put("numberColumns", new String[0]);
            }

            EasyExcel.write(response.getOutputStream(), clazz)
                    .registerWriteHandler(
                            new CommonExcelCellHandler(map))
                    .autoCloseStream(Boolean.FALSE)
                    .sheet(sheetName)
                    .doWrite(data);
        } catch (Exception e) {
            String message = "下载文件失败" + e.getMessage();
            log.error(message, e);
        }
    }
  • 2、传入列名集合方式

/**
     * 导出数据到请求当中,含格式转换handler
     * @param response
     * @param data 数据对象
     * @param sheetName sheet 页名称
     * @param map 需要转换的列名集合,分三类
     */
    public static void exportWithHandler(HttpServletResponse response, List<String[]> data, String sheetName, Map<String, Object> map) {
        try {
            response.setContentType("application/vnd.ms-excel");
            response.setCharacterEncoding("utf-8");
            response.setHeader("Content-disposition", "attachment;filename=" + System.currentTimeMillis() + EXCEL_SUFFIX);

            EasyExcelDataVo easyExcelDataVo = new EasyExcelDataVo(data).invoke();

            List<List<String>> headList = easyExcelDataVo.getHeadList();
            List<List<String>> dataList = easyExcelDataVo.getDataList();

            if (StringUtils.isEmpty(sheetName)) {
                sheetName = DEFAULT_SHEET_NAME;
            }

            EasyExcelFactory.write(response.getOutputStream())
                    .registerWriteHandler(new CommonExcelCellHandler(map))
                    .head(headList)
                    .sheet(sheetName)
                    .doWrite(dataList);
        } catch (Exception e) {
            if (e.getCause() instanceof ClientAbortException) {
                log.warn("创建Excel文件过程中,客户端中断了链接");
                return;
            }

            log.error(CommonConst.UNKNOWN_EXCEPTION, e);
            throw new ApiException(InfoCode.FAIL, "创建Excel文件过程中发生未知错误");
        }
    }

4.3 公共handler代码(含对列宽的处理)

package com.fawm.base.config.easy.excel.write.handler;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import com.fawm.base.exception.ApiException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.CreationHelper;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Workbook;

/**
 * Excel 导出处理日期格式与数值类型 调用端需要将三个参数 dateColumns、dateTimeColumns、numberColumns 传进来,构造方法进行初始化 注意:仅需要对导出的
 * excel 日期筛选以及数值合计有问题的列传入即可,没问题的列可以不用传入 数值类型有问题的,主要原因是 dto 里定义的是 String 类型导致,如果是 double 与 Integer
 * 类型则正常导出不需要传入处理
 *
 * @author liumingxing
 */
@Slf4j
public class CommonExcelCellHandler implements CellWriteHandler {

    /**
     * 列名存储列表 数据量超过500时,会刷新缓存,表头会丢失,所以要存一下,以便查找
     */
    private static final List<String> COLUMNS = new ArrayList<>();

    /**
     * 时间+日期样式存储 一个excel的sheet页,最多可以创建64000个样式,频繁创建会倒是程序报错
     */
    private CellStyle dateTimeStyle;
    /**
     * 时间样式存储 一个excel的sheet页,最多可以创建64000个样式,频繁创建会倒是程序报错
     */
    private CellStyle dateStyle;
    /**
     * 金额样式存储 一个excel的sheet页,最多可以创建64000个样式,频繁创建会倒是程序报错 数值类型有问题的,主要原因是 dto 里定义的是 String 类型导致,如果是
     * double 与 Integer 类型则正常导出不需要传入处理
     */
    private CellStyle numberStyle;

    /**
     * 需要处理日期列名
     */
    private Object dateColumns;
    /**
     * 需要处理日期含时间的列名
     */
    private Object dateTimeColumns;
    /**
     * 需要处理数值的列名
     */
    private Object numberColumns;

    /**
     * 列宽时需要乘256
     */
    private Integer widthCommon = 256;

    /**
     * 初始化参数 注意:数值类型传入前提是列dto里数值类型的字段定义的是字符串类型(如果定义的是double或intger则不需要转换)
     *
     * @param map 传入需要特殊处理的三个类型 dateColumns、dateTimeColumns、numberColumns
     */
    public CommonExcelCellHandler(Map<String, Object> map) {
        COLUMNS.clear();
        dateTimeStyle = null;
        dateStyle = null;
        numberStyle = null;

        this.dateColumns = map.get("dateColumns");
        this.dateTimeColumns = map.get("dateTimeColumns");
        this.numberColumns = map.get("numberColumns");
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder,
            WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex,
            Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder,
            WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex,
            Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder,
            WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head,
            Integer relativeRowIndex, Boolean isHead) {
        // 列宽
        int width = 20;

        if (isHead != null && isHead) {
            //获取表头,存储起来,否则但数据超过500时会 flush 导致列名丢失报错
            COLUMNS.add(cell.getStringCellValue());
        } else if (isHead != null) {
            // 当前列名
            String columnName = COLUMNS.get(cell.getColumnIndex());

            //是否是日期加时间
            boolean dateTimeFlag = StringUtils.isNotBlank(Arrays.stream(
                    (String[]) dateTimeColumns).filter(item -> item.equals(columnName)).findAny()
                    .orElse(""));
            //是否是日期
            boolean dateFlag = StringUtils.isNotBlank(
                    Arrays.stream((String[]) dateColumns).filter(item -> item.equals(columnName))
                            .findAny().orElse(""));
            //是否是数值(前提:要处理的列dto里得是字符串类型)
            boolean numberColumnFlag = StringUtils.isNotBlank(
                    Arrays.stream((String[]) numberColumns).filter(item -> item.equals(columnName))
                            .findAny().orElse(""));

            // 日期类标识
            boolean dateFlags = dateTimeFlag || dateFlag;

            if (dateFlags || numberColumnFlag) {
                Workbook wk = writeSheetHolder.getSheet().getWorkbook();

                String stringCellValue = null;
                try {
                    stringCellValue = cell.getStringCellValue();
                } catch (Exception e) {
                    // double或integer 类型数值不需要转换单元格格式,前端不要传入,后台直接去掉转换解决!
                    dateFlags = false;
                }
                try {
                    CreationHelper createHelper = wk.getCreationHelper();
                    if (StringUtils.isNotBlank(stringCellValue) && dateFlags) {
                        String[] patterns = {"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd"};
                        Date date = DateUtils.parseDate(stringCellValue, patterns);
                        if (dateTimeFlag) {
                            //订单生成日期
                            if (dateTimeStyle == null) {
                                dateTimeStyle = wk.createCellStyle();
                            }
                            dateTimeStyle.setDataFormat(createHelper.createDataFormat()
                                    .getFormat("yyyy/mm/dd hh:mm:ss"));
                            cell.setCellStyle(dateTimeStyle);
                        } else {
                            //其他日期列
                            width = 15;
                            if (dateStyle == null) {
                                dateStyle = wk.createCellStyle();
                            }
                            dateStyle.setDataFormat(
                                    createHelper.createDataFormat().getFormat("yyyy/mm/dd"));
                            cell.setCellStyle(dateStyle);
                        }

                        cell.setCellValue(date);
                    } else if (StringUtils.isNotBlank(stringCellValue) && numberColumnFlag && isNumber(stringCellValue)) {
                        //其他数值列(数值类型有问题的,主要原因是 dto 里定义的是 String 类型导致,如果是 double 与 Integer 类型则正常导出不需要传入处理)
                        width = 11;
                        double money = Double.parseDouble(stringCellValue);
                        if (numberStyle == null) {
                            numberStyle = wk.createCellStyle();
                        }
                        numberStyle
                                .setDataFormat(createHelper.createDataFormat().getFormat("0.00"));
                        cell.setCellStyle(numberStyle);
                        cell.setCellValue(money);
                    }

                    // 统一处理列宽
                    int columnWidth = writeSheetHolder.getSheet()
                            .getColumnWidth(cell.getColumnIndex());
                    if (columnWidth < width * widthCommon) {
                        writeSheetHolder.getSheet()
                                .setColumnWidth(cell.getColumnIndex(), width * widthCommon);
                    }
                } catch (ParseException e) {
                    log.info("导出信息时出现错误:值:{},行:{},列:{}", stringCellValue,
                            cell.getRow().getRowNum(), cell.getColumnIndex());
                    cell.setCellValue("");
                }
            }
        }
    }

    /**
     * 判断一个字符串是否是数字。
     *
     * @param value
     * @return
     */
    public static boolean isNumber(String value) {
        String regex = "^-?\\d+(\\.\\d+)?$";
        Pattern pattern = Pattern.compile(regex);
        return pattern.matcher(value).matches();
    }
}

你可能感兴趣的:(SpringBoot,java基础,EasyExcel)