使用easyExcel 导出带有合计行的excel

项目背景:

产品提了个需求,让把系统中的已存在的几个excel导出加一行合计

实现:

当时想了两种思路;

1. 在业务层把需要合计的手动累加,然后写到最后一行实现合计

2.实现一个工具类,指定需要合计的表头,然后在工具类中对这些列进行合计,基本不改变原有的业务代码,只需要传入需要合计的表头

因为是大量地方使用,所以采用工具类的方式,基本不需要了解之前的业务逻辑

 效果图:

使用easyExcel 导出带有合计行的excel_第1张图片

思路:

1.采用了alibaba的easyExcel,使用填充的方式,需要一个空模板,将数据填充进去,具体请点击查看 easyexcel语雀官方文档

2. 识别出需要合计的列,将这一列除表头外的所有数据进行累加,累加结果写入到新的数据行;

3. 数据格式:

    表头:List> headList,

    填充数据:List> dataList,

    需要合计的表头:HashSet totalHeadSet 合计列不能重复

表头格式:表头只支持一行,里面的泛型List是每一个表头的名称,外面的List>是每一行的所有表头,一定不要把内层的List理解为List<表头>;

示例:

        // 表头格式
        List> rowHead = new ArrayList<>();
        List head = new ArrayList<>();
        head.add("姓名");
        rowHead.add(head);
        List head1 = new ArrayList<>();
        head1.add("身份证");
        rowHead.add(head);
        List head2 = new ArrayList<>();
        head2.add("金额");
        rowHead.add(head);

        // 数据格式
        List> dataList = new ArrayList<>();
        List data = new ArrayList<>();
        data.add("小明");
        data.add("111111");
        data.add("100");
        dataList.add(data);
        List data1 = new ArrayList<>();
        data1.add("大明");
        data1.add("222222");
        data1.add("3600");
        dataList.add(data1);

        // 合计列表头
        HashSet totalHeadSet = new HashSet<>();
        totalHeadSet.add("金额");

问题:

1. 为什么不在第一列写“合计”两个字,便于查看?

    因为不确定第一列是否需要进行合计,如果业务明确不会出现,可以手动修改工具类

package excel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.Data;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import utils.DateUtils;
import utils.TotalRowHandler;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 导出带有合计行的excel
 *
 * @author wcl
 */
@Data
public class ExportExcelUtils {

    /**
     * 合计行数据
     */
    @Data
    static class TotalHeadData {
        /**
         * 表头
         */
        private String head;

        /**
         * 下标
         */
        private Integer index;

        /**
         * 数据
         */
        private String value;
    }

    /**
     * 导出带有合计的动态表头(合计行会添加样式)
     *
     * @param enName       导出excel模板名称
     * @param name         excel文件名称
     * @param dataList     数据
     * @param headList     表头
     * @param totalHeadSet 合计列表头
     * @return excel地址
     */
    public static String exportExcelDynamicAndTotal(String enName, String name, List> dataList, List> headList,
                                                    HashSet totalHeadSet) throws Exception {
        // 模板文件
        InputStream is = ExportExcelUtils.class.getClassLoader().getResourceAsStream("excel/" + enName + "ExportTemplate.xlsx");
        if (is == null) {
            throw new Exception(name + "模板没有找到");
        }
        // 创建需要导出的文件
        String tempFilePath = FileUtils.getSystemTempPath();
        boolean flg = new File(tempFilePath).mkdir();
        String fileName = name + DateUtils.getCurrentDay("yyyyMMddHHmmss") + ".xlsx";
        tempFilePath += fileName;
        File excelFile = new File(tempFilePath);
        try {
            if (!excelFile.exists()) {
                flg = new File(tempFilePath).createNewFile();
            }
        } catch (IOException e) {
            log.error("创建excel文件失败:", e);
            throw new Exception("创建excel文件失败:" + e.getMessage());
        }
        if (CollectionUtils.isEmpty(dataList)) {
            throw new Exception("没有需要导出的数据,请调整检索条件");
        }
        ExcelWriter excelWriter = EasyExcel.write(tempFilePath).head(headList).withTemplate(is).build();
        // 写入合计行样式;默认表头只有一行
        TotalRowHandler totalRowHandler = new TotalRowHandler(dataList.size());
        WriteSheet writeSheet = EasyExcel.writerSheet().registerWriteHandler(totalRowHandler).build();
        addTotalRow(headList, dataList, totalHeadSet);
        // 用填充的方式写入数据
        excelWriter.write(dataList, writeSheet);

        // 关闭流
        excelWriter.finish();
        if (!excelFile.exists()) {
            throw new Exception("导出失败,没有生成导出文件");
        }
        return tempFilePath;
    }

    /**
     * 写入合计行,直接写入导出数据里,数据为空不会写入
     * 先找出所有需要进行合计的列,遍历这一列的数据,进行累加操作,最后将计算出的合计写入到最后一行
     *
     * @param headList     表头
     * @param dataList     数据
     * @param totalHeadSet 需要合计的列表头名称
     */
    private static void addTotalRow(List> headList, List> dataList, HashSet totalHeadSet) {
        if (!CollectionUtils.isEmpty(totalHeadSet)) {
            // 需要合计的列,表头和列下标
            List headIndexData = new ArrayList<>();
            for (int i = 0; i < headList.size(); i++) {
                // i是表头的列下标
                List stringList = headList.get(i);
                String head = stringList.get(0);
                // 需要合计的表头
                if (totalHeadSet.contains(head)) {
                    TotalHeadData headData = new TotalHeadData();
                    headData.setHead(head);
                    headData.setIndex(i);
                    headIndexData.add(headData);
                }
            }
            // 下标-数据对象
            Map indexTotalHeadData = headIndexData
                    .parallelStream()
                    .collect(Collectors.toMap(TotalHeadData::getIndex, v -> v));
            // 遍历行
            for (List rowList : dataList) {
                // 遍历列
                for (int i = 0; i < rowList.size(); i++) {
                    if (indexTotalHeadData.containsKey(i)) {
                        TotalHeadData totalHeadData = indexTotalHeadData.get(i);
                        // 累计数据
                        String valueTotal = totalHeadData.getValue();
                        BigDecimal total = BigDecimal.ZERO;
                        if (StringUtils.isNotEmpty(valueTotal)) {
                            try {
                                total = new BigDecimal(valueTotal);
                            } catch (Exception e) {
                                log.error("String转换BigDecimal失败,累计值:{},错误信息:{}", valueTotal, e.getMessage());
                            }
                        }
                        // 本次数据
                        String valueNow = rowList.get(i);
                        BigDecimal decimalNow = BigDecimal.ZERO;
                        if (StringUtils.isNotEmpty(valueNow)) {
                            try {
                                decimalNow = new BigDecimal(valueNow);
                            } catch (Exception e) {
                                log.error("String转换BigDecimal失败,单元格内容:{},错误信息:{}", valueNow, e.getMessage());
                            }
                        }
                        total = total.add(decimalNow);
                        totalHeadData.setValue(String.valueOf(total));
                    }
                }
            }
            // 写入合计行
            List totalRow = new ArrayList<>();
            // 写入行数据;如果业务明确,可以将此处改为fori,在第一列写入“合计”二字,便于查看
            for (List ignored : headList) {
                String value = "";
                totalRow.add(value);
            }
            // 修改行数据
            for (int i = 0; i < totalRow.size(); i++) {
                if (indexTotalHeadData.containsKey(i)) {
                    TotalHeadData totalHeadData = indexTotalHeadData.get(i);
                    totalRow.set(i, totalHeadData.getValue());
                }
            }
            dataList.add(totalRow);
        }
    }


}
package utils;

import org.apache.commons.lang.StringUtils;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * 日期处理工具类
 *
 * @author wcl
 */
public class DateUtils {
    /**
     * 获取当前日期
     *
     * @param pattern 格式,默认格式yyyyMMdd
     * @return 20190101
     */
    public static String getCurrentDay(String pattern) {
        LocalDateTime localDateTime = LocalDateTime.now();
        if (StringUtils.isEmpty(pattern)) {
            pattern = "yyyyMMdd";
        }
        return format(localDateTime2Date(localDateTime), pattern);
    }

    /**
     * 格式化日期为字符串
     *
     * @param date    date
     * @param pattern 格式
     * @return 日期字符串
     */
    public static String format(Date date, String pattern) {
        if (date == null) {
            return null;
        }
        Instant instant = date.toInstant();
        LocalDateTime localDateTime = LocalDateTime.ofInstant(instant, ZoneId.systemDefault());
        return localDateTime.format(DateTimeFormatter.ofPattern(pattern));
    }

    /**
     * LocalDateTime类型转为Date
     *
     * @param localDateTime LocalDateTime object
     * @return Date object
     */
    public static Date localDateTime2Date(LocalDateTime localDateTime) {
        return Date.from(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    }
}

 设置合计行的样式

package utils;

import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.util.StyleUtil;
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.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;

import java.util.List;

/**
 * 合计行设置样式
 *
 * @author wcl
 */
@Slf4j
public class TotalRowHandler implements CellWriteHandler {

    /**
     * 开始添加样式的行下标
     */
    private Integer startRow;

    public TotalRowHandler(Integer startRow) {
        this.startRow = startRow;
    }

    @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 afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData,
                                       Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List cellDataList,
                                 Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 非表头项
        if (!isHead) {
            if (cell != null) {
                // 从数据开始,表头不会读取
                if (relativeRowIndex >= startRow) {
                    if (head != null) {
                        log.debug("表头====》" + head.getHeadNameList().get(0));
                    }
                    if (CellType.STRING.name().equals(cell.getCellTypeEnum().name())) {
                        log.debug("单元格数据===》" + cell.getStringCellValue());
                    } else if (CellType.NUMERIC.name().equals(cell.getCellTypeEnum().name())) {
                        log.debug("单元格数据===》" + cell.getNumericCellValue());
                    }
                    log.info("");
                    // 设置样式
                    Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
                    // 设置字体样式
                    WriteCellStyle writeCellStyle = new WriteCellStyle();
                    WriteFont headWriteFont = new WriteFont();
                    headWriteFont.setFontName("宋体");
                    headWriteFont.setFontHeightInPoints((short) 14);
                    headWriteFont.setBold(true);
                    writeCellStyle.setWriteFont(headWriteFont);
                    // 设置背景色
                    writeCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());

                    // 样式写入单元格
                    CellStyle cellStyle = StyleUtil.buildHeadCellStyle(workbook, writeCellStyle);
                    cell.setCellStyle(cellStyle);
                }
            }
        }

    }
}

你可能感兴趣的:(工具类,Java,java,excel)