EasyExcel导出合并单元格

前言

功能展示

功能类似这样:

一、为什么选择Alibaba EasyExcel

优势:

EasyExcel 重写了poi对07版Excel的解析,并且再大的excel也不会出现内存溢出

导入EasyExcel依赖

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.5</version>
</dependency>

二、easyexcel 注解合并单元格

@ContentLoopMerge:标注在字段上

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface ContentLoopMerge {

    /**
     * 合并行
     *
     * @return
     */
    int eachRow() default 1;

    /**
     * 合并列
     *
     * @return
     */
    int columnExtend() default 1;
}

OnceAbsoluteMerge:标注在类上

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface OnceAbsoluteMerge {

    /**
     * 初始行
     *
     * @return
     */
    int firstRowIndex() default -1;

    /**
     * 最后一行
     *
     * @return
     */
    int lastRowIndex() default -1;

    /**
     * 初始列
     *
     * @return
     */
    int firstColumnIndex() default -1;

    /**
     * 最后一列
     *
     * @return
     */
    int lastColumnIndex() default -1;
}

2.1 @ContentLoopMerge示例

@Data
public class Demo implements Serializable {

    @ExcelProperty(value = "商户名称", index = 0)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String appName;

    @ExcelProperty(value = "城市名称", index = 1)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    @ContentLoopMerge(eachRow = 2, columnExtend = 3)
    private String cityName;

    @ExcelProperty(value = "区域名称", index = 2)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String regionName;

    @ExcelProperty(value = "商圈名称", index = 3)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String businessAreaName;

    @ExcelProperty(value = "楼盘名称", index = 4)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String gardenName;

    @ExcelProperty(value = "楼栋名称", index = 5)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String buildingName;

    @ExcelProperty(value = "单元名称", index = 6)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String unitName;

    @ExcelProperty(value = "价格", index = 7)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private Integer price;

}

2.2 @OnceAbsoluteMerge示例

@Data
@OnceAbsoluteMerge(firstRowIndex = 1, lastRowIndex = 3 , firstColumnIndex = 1 , lastColumnIndex = 3)
public class Demo implements Serializable {

    @ExcelProperty(value = "商户名称", index = 0)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String appName;

    @ExcelProperty(value = "城市名称", index = 1)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String cityName;

    @ExcelProperty(value = "区域名称", index = 2)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String regionName;

    @ExcelProperty(value = "商圈名称", index = 3)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String businessAreaName;

    @ExcelProperty(value = "楼盘名称", index = 4)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String gardenName;

    @ExcelProperty(value = "楼栋名称", index = 5)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String buildingName;

    @ExcelProperty(value = "单元名称", index = 6)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String unitName;

    @ExcelProperty(value = "价格", index = 7)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private Integer price;

}

注意:

  • @ContentLoopMerge(eachRow = 2, columnExtend = 3) 可以合并单元格,但是他是按指定行数和指定列数去合并,并不能实现内容相同的合并。
  • @OnceAbsoluteMerge(firstRowIndex = 1, lastRowIndex = 3 , firstColumnIndex = 1 , lastColumnIndex = 3) 可以合并单元格,但是他是按指定行数和指定列数去合并,并不能实现内容相同的合并。
  • @ExcelProperty(value = {"学生信息","备注"},能实现多个标题,但标题是固定的,不是动态的。

三、自定义改造

3.1 导出的实体类,也就是表头

@Data
public class Demo implements Serializable {

    @ExcelProperty(value = "商户名称", index = 0)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String appName;

    @ExcelProperty(value = "城市名称", index = 1)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String cityName;

    @ExcelProperty(value = "区域名称", index = 2)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String regionName;

    @ExcelProperty(value = "商圈名称", index = 3)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String businessAreaName;

    @ExcelProperty(value = "楼盘名称", index = 4)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String gardenName;

    @ExcelProperty(value = "楼栋名称", index = 5)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String buildingName;

    @ExcelProperty(value = "单元名称", index = 6)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private String unitName;

    @ExcelProperty(value = "价格", index = 7)
    @ColumnWidth(15)
    @ContentStyle(verticalAlignment = VerticalAlignmentEnum.CENTER, horizontalAlignment = HorizontalAlignmentEnum.CENTER)
    private Integer price;

}

3.2 行合并工具类

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @Author: huangyibo
 * @Date: 2023/3/30 9:45
 * @Description: Excel单元格行合并处理策略
 */

public class ExcelFillCellRowMergeStrategy implements CellWriteHandler {

    //数组存放这一行需要合并那几列  [0,1,2] 在这mergeRowIndex行中合并 0、1、2列
    private int[] mergeColumnIndex;

    // 存放需要向上合并的行
    private int mergeRowIndex;

    // 不要合并的行
    private Integer noMergeRowIndex;

    public ExcelFillCellRowMergeStrategy() {
    }

    public ExcelFillCellRowMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex) {
        this.mergeRowIndex = mergeRowIndex;
        this.mergeColumnIndex = mergeColumnIndex;
    }

    public ExcelFillCellRowMergeStrategy(int mergeRowIndex, int[] mergeColumnIndex, Integer noMergeRowIndex) {
        this.mergeColumnIndex = mergeColumnIndex;
        this.mergeRowIndex = mergeRowIndex;
        this.noMergeRowIndex = noMergeRowIndex;
    }

    @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, WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
                                 List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        int curRowIndex = cell.getRowIndex();
        int curColIndex = cell.getColumnIndex();
        if (curRowIndex > mergeRowIndex) {
            for (int i = 0; i < mergeColumnIndex.length; i++) {
                if (curColIndex == mergeColumnIndex[i]) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }

    /**
     * 当前单元格向上合并
     * .
     * @param writeSheetHolder writeSheetHolder
     * @param cell             当前单元格
     * @param curRowIndex      当前行
     * @param curColIndex      当前列
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        Row  preRow = cell.getSheet().getRow(curRowIndex - 1);
        if (preRow == null) {
            // 当获取不到上一行数据时,使用缓存sheet中数据
            preRow = writeSheetHolder.getCachedSheet().getRow(curRowIndex - 1);
        }
        Cell preCell = preRow.getCell(curColIndex);
        Object preData = preCell.getCellType() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
        //不需要合并的列直接跳出
        if ((noMergeRowIndex != null) && noMergeRowIndex == (curRowIndex - 1)){
            return;
        }
        // 将当前单元格数据与上一个单元格数据比较
        boolean dataBool = preData.equals(curData);

        //此处需要注意:所以获取每一行第一列数据和上一行第一列数据进行比较,如果相等合并
        boolean equals = cell.getRow().getCell(0).getStringCellValue().equals(cell.getSheet().getRow(curRowIndex - 1).getCell(0).getStringCellValue());
        if (dataBool && equals) {
            Sheet sheet = writeSheetHolder.getSheet();
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // 若上一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastRow(curRowIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 若上一个单元格未被合并,则新增合并单元
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }
}

3.3 列合并的工具类

@Data
@AllArgsConstructor
public class CellLineRange {

    /**
     * 起始列
     */
    private int firstCol;

    /**
     * 结束列
     */
    private int lastCol;
}
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;

import java.util.List;

/**
 * @Author: huangyibo
 * @Date: 2023/3/30 10:30
 * @Description: Excel单元格列合并处理策略
 */

public class ExcelFillCelColumnMergeStrategy implements CellWriteHandler {

    //自定义合并单元格的列 如果想合并 第4列和第5例 、第6列和第7例: [CellLineRange(firstCol=3, lastCol=4), CellLineRange(firstCol=5, lastCol=6)]
    private List<CellLineRange> cellLineRangeList;

    //自定义合并单元格的开始的行  一般来说填表头行高0 表示从表头下每列开始合并 :如表头行高位为3则 int mergeRowIndex = 2  ;
    private int mergeRowIndex;

    public ExcelFillCelColumnMergeStrategy(int mergeRowIndex, List<CellLineRange> cellLineRangeList) {
        this.mergeRowIndex=mergeRowIndex;
        this.cellLineRangeList=cellLineRangeList;
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {

    }

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

    }

    @Override
    public void afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, WriteCellData<?> cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer integer, Boolean aBoolean) {

        //当前单元格的行数
        int curRowIndex = cell.getRowIndex();
        // 当前单元格的列数
        int curColIndex = cell.getColumnIndex();
        if (curRowIndex > mergeRowIndex) {
            for (int i = 0; i < cellLineRangeList.size(); i++) {
                if (curColIndex > cellLineRangeList.get(i).getFirstCol()&&curColIndex<=cellLineRangeList.get(i).getLastCol()) {
                    //单元格数据处理
                    mergeWithLeftLine(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }
    }

    /**
     * @description 当前单元格向左合并
     */
    private void mergeWithLeftLine(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        //当前单元格中数据
        Object curData = cell.getCellType() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        //获取当前单元格的左面一个单元格
        Cell leftCell = cell.getSheet().getRow(curRowIndex).getCell(curColIndex - 1);
        //获取当前单元格的左面一个单元格中的数据
        Object leftData = leftCell.getCellType() == CellType.STRING ? leftCell.getStringCellValue() : leftCell.getNumericCellValue();

        // 将当前单元格数据与左侧一个单元格数据比较
        if (leftData.equals(curData)) {
            //获取当前sheet页
            Sheet sheet = writeSheetHolder.getSheet();
            //得到所有的合并单元格
            List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
            //是否合并
            boolean isMerged = false;
            for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                //CellRangeAddress POI合并单元格
                //CellRangeAddress(int firstRow, int lastRow, int firstCol, int lastCol)
                //例子:CellRangeAddress(2, 6000, 3, 3);
                //第2行起 第6000行终止 第3列开始 第3列结束。
                CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                // cellRangeAddr.isInRange(int rowInd, int colInd)确定给定坐标是否在此范围的范围内。
                // 若左侧一个单元格已经被合并,则先移出原有的合并单元,再重新添加合并单元
                if (cellRangeAddr.isInRange(curRowIndex, curColIndex - 1)) {
                    sheet.removeMergedRegion(i);
                    cellRangeAddr.setLastColumn(curColIndex);
                    sheet.addMergedRegion(cellRangeAddr);
                    isMerged = true;
                }
            }
            // 若左侧一个单元格未被合并,则新增合并单元
            if (!isMerged) {
                CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex , curRowIndex, curColIndex- 1, curColIndex);
                sheet.addMergedRegion(cellRangeAddress);
            }
        }
    }
}

3.4 EasyExcelUtil 工具类

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.read.metadata.ReadSheet;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.kfang.web.price.manager.excel.ImportDataAbstractListener;
import com.kfang.web.price.manager.excel.ImportDataReturnVo;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;

/**
 * @author: huangyibo
 * @Date: 2022/3/24 20:11
 * @Description: EasyExcelUtil 工具类
 */

public class EasyExcelUtil {

    /**
     * 下载文件时,针对不同浏览器,进行附件名的编码
     *
     * @param filename 下载文件名
     *
     * @param agent 客户端浏览器
     *
     * @return 编码后的下载附件名
     * @throws IOException
     */
    public static String encodeDownloadFilename(String filename, String agent)
            throws IOException {
        if (agent.contains("Firefox")) { // 火狐浏览器
            filename = "=?UTF-8?B?"
                    + Base64.getEncoder().encode(filename.getBytes("utf-8"))
                    + "?=";
            filename = filename.replaceAll("\r\n", "");
        } else { // IE及其他浏览器
            filename = URLEncoder.encode(filename, "utf-8");
            filename = filename.replace("+"," ");
        }
        return filename;
    }


    /**
     * 设置导出Excel格式
     * @param response
     * @param request
     * @param filename
     * @throws IOException
     */
    public static void setExportExcelFormat(HttpServletResponse response, HttpServletRequest request, String filename) throws IOException {
        String agent = request.getHeader("user-agent");//获得游览器
        filename = filename + ".xlsx";
        String downloadFilename = encodeDownloadFilename(filename, agent); //使用工具类解决文件乱码的问题
        response.setCharacterEncoding("UTF-8");
        // 设置响应输出的头类型
        response.setHeader("content-Type", "application/vnd.ms-excel");
        // 下载文件的默认名称
        response.setHeader("Content-Disposition", "attachment;filename="+downloadFilename);
        response.setHeader("Pragma", "public");
        response.setHeader("Cache-Control", "no-store");
        response.addHeader("Cache-Control", "max-age=0");
    }


    /**
     * 读取导入excel数据(单sheet页读取)
     * @param file              上传文件
     * @param clazz             导入实体类型
     * @param excelListener     数据导入监听器
     * @return ImportDataReturnVo<?>
     * @throws IOException
     */
    public static ImportDataReturnVo<?> read(MultipartFile file, Class<?> clazz, ImportDataAbstractListener<?> excelListener) throws IOException {
        EasyExcel.read(file.getInputStream(), clazz, excelListener).sheet().doRead();
        return excelListener.getResult();
    }


    /**
     * 读取导入excel数据(读取全部sheet页)
     * @param file              上传文件
     * @param clazz             导入实体类型
     * @param excelListener     数据导入监听器
     * @return ImportDataReturnVo<?>
     * @throws IOException
     */
    public static ImportDataReturnVo<?> readAll(MultipartFile file, Class<?> clazz, ImportDataAbstractListener<?> excelListener) throws IOException {
        EasyExcel.read(file.getInputStream(), clazz, excelListener).doReadAll();
        return excelListener.getResult();
    }


    /**
     * 读取导入excel数据(读取指定sheet页)
     * @param file              上传文件
     * @param clazz             导入实体类型
     * @param excelListener     数据导入监听器
     * @param sheetNumList      指定的sheetNum
     * @return ImportDataReturnVo<?>
     * @throws IOException
     */
    public static ImportDataReturnVo<?> readSheet(MultipartFile file, Class<?> clazz, ImportDataAbstractListener<?> excelListener, List<Integer> sheetNumList) throws IOException {
        ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build();
        // 读取不同的数据, 需要使用不同的head 和Listener
        List<ReadSheet> readSheetList = new ArrayList<>();
        sheetNumList.forEach(sheetNum -> {
            // readSheet参数设置读取sheet的序号
            ReadSheet readSheet = EasyExcel.readSheet(sheetNum).head(clazz).registerReadListener(excelListener).build();
            readSheetList.add(readSheet);
        });
        // 这里注意 一定要把readSheetList一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
        excelReader.read(readSheetList);
        // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
        excelReader.finish();
        return excelListener.getResult();
    }

    /**
     * 将数据写入Excel并写入流中
     * @param outputStream  输出流
     * @param clazz         导出实体类型
     * @param list          导出数据集合
     * @param sheetName     sheet页名称
     */
    public static void writeExcel2Stream(ByteArrayOutputStream outputStream, Class<?> clazz, List<?> list, String sheetName){
        EasyExcel.write(outputStream, clazz).sheet(sheetName).doWrite(list);
    }

    /**
     * 单sheet页写入Excel
     * @param outputStream  输出流
     * @param clazz         导出实体类型
     * @param list          导出数据集合
     * @param sheetName     sheet页名称
     */
    public static void writeExcel(ServletOutputStream outputStream, Class<?> clazz, List<?> list, String sheetName) {
        EasyExcel.write(outputStream, clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
                .sheet(sheetName)
                .doWrite(list);
    }

    /**
     * 单sheet页写入Excel-合并策略
     * @param outputStream          输出流
     * @param clazz                 导出实体类型
     * @param list                  导出数据集合
     * @param sheetName             sheet页名称
     * @param cellWriteHandlerList  合并策略集合
     */
    public static void writeExcelMerge(ServletOutputStream outputStream, Class<?> clazz, List<?> list, String sheetName, List<CellWriteHandler> cellWriteHandlerList) {
        ExcelWriterBuilder excelWriterBuilder = EasyExcel.write(outputStream, clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        if(!CollectionUtils.isEmpty(cellWriteHandlerList)){
            for (CellWriteHandler cellWriteHandler : cellWriteHandlerList) {
                excelWriterBuilder.registerWriteHandler(cellWriteHandler);
            }
        }
        excelWriterBuilder.sheet(sheetName).doWrite(list);
    }

    /**
     * 多sheet页写入Excel
     * @param excelWriter   excelWriter写出对象
     * @param clazz         导出实体类型
     * @param list          导出数据集合
     * @param num           sheet页码
     * @param sheetName     sheet页名称
     */
    public static void writerSheetExcel(ExcelWriter excelWriter, Class<?> clazz, List<?> list, Integer num, String sheetName) {
        WriteSheet writeSheet = EasyExcel.writerSheet(num, sheetName).head(clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).build();
        excelWriter.write(list, writeSheet);
    }


    /**
     * 多sheet页写入Excel-合并策略
     * @param excelWriter           excelWriter写出对象
     * @param clazz                 导出实体类型
     * @param list                  导出数据集合
     * @param num                   sheet页码
     * @param sheetName             sheet页名称
     * @param cellWriteHandlerList  合并策略集合
     */
    public static void writerSheetExcelMerge(ExcelWriter excelWriter, Class<?> clazz, List<?> list, Integer num, String sheetName, List<CellWriteHandler> cellWriteHandlerList) {
        ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.writerSheet(num, sheetName).head(clazz)
                .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy());
        if(!CollectionUtils.isEmpty(cellWriteHandlerList)){
            for (CellWriteHandler cellWriteHandler : cellWriteHandlerList) {
                excelWriterSheetBuilder.registerWriteHandler(cellWriteHandler);
            }
        }
        WriteSheet writeSheet = excelWriterSheetBuilder.build();
        excelWriter.write(list, writeSheet);
    }
}

3.5 单sheet页写入Excel-合并策略

  • ExcelFillCellRowMergeStrategy和ExcelFillCelColumnMergeStrategy可以单独使用,也可以组合使用,这里演示组合使用
@Lazy
@RestController
@RequestMapping(value = "/demo", produces = {"application/json;charset=UTF-8"})
@Slf4j
@Api(tags = ApiSwaggerTags.DICT_ROOM_RULE_APPRAISAL,value = "规则管理")
public class DictRoomRuleController extends WebBaseController {

    @PostMapping(value = "/exportMerge")
    @ApiOperation(value = "导出测试信息")
    @ApiResponses({
            @ApiResponse(message = "导出测试信息", code = 200, response = Boolean.class)
    })
    public void exportMerge(HttpServletResponse response, HttpServletRequest request) {
        try {
            ServletOutputStream outputStream = response.getOutputStream();

            List<Demo> list = new ArrayList<>();
            buildData(list);

            EasyExcelUtil.setExportExcelFormat(response, request,"测试数据");

            //需要合并的列
            int[] mergeColumnIndex = {0, 1, 2, 3, 4};

            //设置第几行开始合并
            int mergeRowIndex = 1;

            // Excel单元格行合并处理策略
            ExcelFillCellRowMergeStrategy rowMergeStrategy = new ExcelFillCellRowMergeStrategy(mergeRowIndex, mergeColumnIndex);

            //列合并的工具实体类
            ArrayList<CellLineRange> cellLineRanges=new ArrayList<>();
            cellLineRanges.add(new CellLineRange(0,2));
            // Excel单元格列合并处理策略
            ExcelFillCelColumnMergeStrategy celColumnMergeStrategy = new ExcelFillCelColumnMergeStrategy(0, cellLineRanges);

            List<CellWriteHandler> cellWriteHandlerList = Stream.of(celColumnMergeStrategy, rowMergeStrategy).collect(Collectors.toList());

            EasyExcelUtil.writeExcelMerge(outputStream, Demo.class, list, "测试数据", cellWriteHandlerList);

            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            log.error("楼盘估价数据导出excel Exception", e);
        }
    }

    /**
     * 构建数据
     * @param list
     */
    private void buildData (List<Demo> list){
        for (int i = 0; i < 5; i++) {
            Demo demo = new Demo();
            demo.setAppName("app1");
            demo.setCityName("app1");
            demo.setRegionName("app1");
            demo.setBusinessAreaName("深大");
            demo.setGardenName("大冲国际中心");
            demo.setBuildingName("一期");
            demo.setUnitName("A座");
            demo.setPrice(100000 + i);
            list.add(demo);
        }

        for (int i = 0; i < 2; i++) {
            Demo demo = new Demo();
            demo.setAppName("app2");
            demo.setCityName("深圳");
            demo.setRegionName("南山区");
            demo.setBusinessAreaName("前海湾");
            demo.setGardenName("前海中心大厦");
            demo.setBuildingName("一期");
            demo.setUnitName("B座");
            demo.setPrice(100000 + i);
            list.add(demo);
        }

        for (int i = 0; i < 2; i++) {
            Demo demo = new Demo();
            demo.setAppName("深圳");
            demo.setCityName("深圳");
            demo.setRegionName("深圳");
            demo.setBusinessAreaName("后海");
            demo.setGardenName("中国华润大厦");
            demo.setBuildingName("中国华润大厦");
            demo.setUnitName("A座");
            demo.setPrice(100000 + i);
            list.add(demo);
        }

        for (int i = 0; i < 1; i++) {
            Demo demo = new Demo();
            demo.setAppName("app3");
            demo.setCityName("深圳");
            demo.setRegionName("宝安区");
            demo.setBusinessAreaName("壹方城");
            demo.setGardenName("壹方中心");
            demo.setBuildingName("一期");
            demo.setUnitName("A座");
            demo.setPrice(100000 + i);
            list.add(demo);
        }
    }
}

3.6 多sheet页写入Excel-合并策略

  • ExcelFillCellRowMergeStrategy和ExcelFillCelColumnMergeStrategy可以单独使用,也可以组合使用,这里演示组合使用
@Lazy
@RestController
@RequestMapping(value = "/demo", produces = {"application/json;charset=UTF-8"})
@Slf4j
@Api(tags = ApiSwaggerTags.DICT_ROOM_RULE_APPRAISAL,value = "规则管理")
public class DictRoomRuleController extends WebBaseController {

    @PostMapping(value = "/exportMergeTest")
    @ApiOperation(value = "导出测试信息")
    @ApiResponses({
            @ApiResponse(message = "导出测试信息", code = 200, response = Boolean.class)
    })
    public void exportMergeTest(HttpServletResponse response, HttpServletRequest request) {
        try {
            ServletOutputStream outputStream = response.getOutputStream();

            List<List<Demo>> list = new ArrayList<>();
            buildListData(list);

            EasyExcelUtil.setExportExcelFormat(response, request,"KF楼盘估价");

            //需要合并的列
            int[] mergeColumnIndex = {0, 1, 2, 3, 4};

            //设置第几行开始合并
            int mergeRowIndex = 1;

            // Excel单元格行合并处理策略
            ExcelFillCellRowMergeStrategy rowMergeStrategy = new ExcelFillCellRowMergeStrategy(mergeRowIndex, mergeColumnIndex);

            //必须放到循环外,否则会刷新流
            ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
            for (int i = 0; i < list.size(); i++) {
                //sheet页码
                int num = 1 + 1;

                //列合并的工具实体类
                ArrayList<CellLineRange> cellLineRanges=new ArrayList<>();
                cellLineRanges.add(new CellLineRange(0,2));

                // Excel单元格列合并处理策略
                ExcelFillCelColumnMergeStrategy celColumnMergeStrategy = new ExcelFillCelColumnMergeStrategy(0, cellLineRanges);
                List<CellWriteHandler> cellWriteHandlerList = Stream.of(celColumnMergeStrategy, rowMergeStrategy).collect(Collectors.toList());
                EasyExcelUtil.writerSheetExcelMerge(excelWriter, Demo.class, list.get(i), num, "楼盘估价"+num, cellWriteHandlerList);
            }

            //刷新流
            excelWriter.finish();

            outputStream.flush();
            outputStream.close();
        } catch (Exception e) {
            log.error("楼盘估价数据导出excel Exception", e);
        }
    }

    /**
     * 构建数据
     * @param list
     */
    private void buildListData (List<List<Demo>> list){
        List<Demo> list1 = new ArrayList<>();
        List<Demo> list2 = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            Demo demo = new Demo();
            demo.setAppName("app1");
            demo.setCityName("app1");
            demo.setRegionName("app1");
            demo.setBusinessAreaName("深大");
            demo.setGardenName("大冲国际中心");
            demo.setBuildingName("一期");
            demo.setUnitName("A座");
            demo.setPrice(100000 + i);
            list1.add(demo);
        }

        for (int i = 0; i < 2; i++) {
            Demo demo = new Demo();
            demo.setAppName("app2");
            demo.setCityName("深圳");
            demo.setRegionName("南山区");
            demo.setBusinessAreaName("前海湾");
            demo.setGardenName("前海中心大厦");
            demo.setBuildingName("一期");
            demo.setUnitName("B座");
            demo.setPrice(100000 + i);
            list1.add(demo);
        }

        for (int i = 0; i < 2; i++) {
            Demo demo = new Demo();
            demo.setAppName("深圳");
            demo.setCityName("深圳");
            demo.setRegionName("深圳");
            demo.setBusinessAreaName("后海");
            demo.setGardenName("中国华润大厦");
            demo.setBuildingName("中国华润大厦");
            demo.setUnitName("A座");
            demo.setPrice(100000 + i);
            list2.add(demo);
        }

        for (int i = 0; i < 1; i++) {
            Demo demo = new Demo();
            demo.setAppName("app3");
            demo.setCityName("深圳");
            demo.setRegionName("宝安区");
            demo.setBusinessAreaName("壹方城");
            demo.setGardenName("壹方中心");
            demo.setBuildingName("一期");
            demo.setUnitName("A座");
            demo.setPrice(100000 + i);
            list2.add(demo);
        }
        list.add(list1);
        list.add(list2);
    }
}

3.7 动态标题

  • 生成动态标题
//生成动态标题
private List<List<String>> getHeaders(String dynamicTitle) {
    List<List<String>> headers=new ArrayList<>();
    List<String> schoolHead=new ArrayList<>();
    schoolHead.add(dynamicTitle);
    schoolHead.add("学校");
    List<String> nameHead=new ArrayList<>();
    nameHead.add(dynamicTitle);
    nameHead.add("姓名");
    List<String> sexHead=new ArrayList<>();
    sexHead.add(dynamicTitle);
    sexHead.add("性别");
    List<String> ageHead=new ArrayList<>();
    ageHead.add(dynamicTitle);
    ageHead.add("年龄");
    List<String> cityHead=new ArrayList<>();
    cityHead.add(dynamicTitle);
    cityHead.add("城市");
    List<String> remarksHead=new ArrayList<>();
    remarksHead.add(dynamicTitle);
    remarksHead.add("备注");
    headers.add(schoolHead);
    headers.add(nameHead);
    headers.add(sexHead);
    headers.add(ageHead);
    headers.add(cityHead);
    headers.add(remarksHead);
    return headers;
}   
  • 设置动态标题
//设置动态标题
List<List<String>> headers = this.getHeaders("学生信息" + dynamicTitle);

EasyExcel.write(outputStream, clazz)
        .sheet(sheetName)
        .head(headers)
        .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
        .doWrite(list); 

参考:
https://easyexcel.opensource.alibaba.com/index.html

https://blog.csdn.net/weixin_48788601/article/details/127323681

https://blog.csdn.net/qq_39032203/article/details/125405411

https://huaweicloud.csdn.net/63876534dacf622b8df8b399.html

https://www.freesion.com/article/90581470133/

https://blog.csdn.net/weixin_43296313/article/details/124814674

https://blog.csdn.net/weixin_55383903/article/details/122624251

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容