使用的前提

jdk 1.8

spring-boot 2.6.13

easyexcel 2.2.10 (easyexcel 3.x 略有不同)

生成二维码需要的两个依赖

com.google.zxing 3.4.1

hutool 5.2.2

最终效果

模板


最终导出效果(二维码做了一些拉伸)

image.png

引入依赖

<!--        web启动器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
<!--        lombok依赖-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
<!--        spring-boot-starter-test依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
<!--        easyexcel依赖-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.2.10</version>
        </dependency>
        <!--二维码需要使用到的-->
        <dependency>
            <groupId>com.google.zxing</groupId>
            <artifactId>core</artifactId>
            <version>3.4.1</version>
        </dependency>
<!--        hutool工具类-->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.2.2</version>
        </dependency>

实际的实现方式

在写模板的时候,如果是从list中获取的话,就用{.xxxxx}(注意有个 .),如果是之填充一个的话就没有这个“.”了用{xxxxx}表示;

在代码中写的话要用一下这种方式

// 准备数据
// 定义需要导出的 实体list
List<EasyexcelExportModel> easyexcelExportModels = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    EasyexcelExportModel model = new EasyexcelExportModel();
    model.setId(i);
    model.setName("name" + i);
    model.setAge(i*10);
    // 添加二维码,把二维码转为byte[]
    byte[] qrCode = QrCodeUtil.generatePng(i+"1",
                    QrConfig.create().setCharset(Charset.forName("utf-8")));
    model.setQrCode(qrCode);
    easyexcelExportModels.add(model);
}
// 添加需要导出的特殊字段值
Map<String,Object> map = new HashMap<>();
map.put("operator","操作员007");
map.put("Signer","签收员009");
map.put("bigQrCode",QrCodeUtil.generatePng("bigQrCode",
                QrConfig.create().setCharset(Charset.forName("utf-8"))));


// 模板的输入流
InputStream projectPath =  this.getClass().getClassLoader().getResourceAsStream("excelTemplate"+ File.separator+ "exportExcelByTemplate.xlsx");
// 通过输出流的方式将写成的excel返回给前端
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
//防止中文乱码
response.setHeader("Content-disposition", "attachment;filename=" + System.currentTimeMillis() + ".xlsx");

ExcelWriter excelWriter = EasyExcel
// 写入到输出流当中
.write(response.getOutputStream())
// 携带的模板信息
.withTemplate(projectPath).build();

// 将特殊字段的值赋值
excelWriter.fill(map,writeSheet);
excelWriter.fill(easyexcelExportModels,writeSheet);

如果模板中最后一行有特殊含义

例如:最后一行是签收员image.png
这时候在 创建excel的时候就需要加一个配置了

// 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置
        //forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
        // 如果数据量大 list不是最后一行 参照下一个
        FillConfig fillConfig =
                FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        excelWriter.fill(easyexcelExportModels,fillConfig,writeSheet);

下面这个是easyexcel的官方文档 也有提到
填充Excel | Easy Excel
image.png

重点,本项目当中需要将生成的二维码进行放大

**这里大致的意思就是,**
**1.在数据转化的时候,自己需要进行拦截一下,不让easyexcel自动生成;**
**2.在最后都转化完成之后,自己在生成一个图片放在画布上**
这里就需要重写一个方法了,即new CellWriteHandler()

ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
                .registerWriteHandler(
                        new 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 afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
                                // 因为导出的 二维码图片都在第 4列,这里先将图片列设置为空;
                                // 因为如果这里不设置为空的话,后面再根据这个拉伸的话,这小二维码还会存在
                                int columnIndex = cell.getColumnIndex();
                                byte[] imageValue = cellData.getImageValue();
                                if ((3==columnIndex || 7 == columnIndex) && imageValue != null) {
                                    cellData.setType(CellDataTypeEnum.EMPTY);
                                }
                            }
                            // 在单元上的所有操作完成后调用
                            @Override
                            public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

                                // 设置样式
                                // 获取当前sheet
                                Sheet sheet = cell.getSheet();
                                int columnIndex = cell.getColumnIndex();
                                if (columnIndex == 3) {
                                    // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上
                                    int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG);
                                    Drawing drawing = sheet.getDrawingPatriarch();
                                    // 如果画布为空就创建一个
                                    if (drawing == null) {
                                        drawing = sheet.createDrawingPatriarch();
                                    }
                                    CreationHelper helper = sheet.getWorkbook().getCreationHelper();
                                    ClientAnchor anchor = helper.createClientAnchor();
                                    // 设置图片坐标
                                    anchor.setDx1(0);
                                    anchor.setDx2(0);
                                    anchor.setDy1(0);
                                    anchor.setDy2(0);
                                    //设置图片位置
                                    anchor.setCol1(cell.getColumnIndex());
                                    anchor.setCol2(cell.getColumnIndex() + 2);
                                    anchor.setRow1(cell.getRowIndex());
                                    anchor.setRow2(cell.getRowIndex()+1);
                                    // 设置图片可以随着单元格移动
                                    anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
//                                    drawing.createPicture(anchor, 0);
                                    drawing.createPicture(anchor,index);
                                } else if (columnIndex == 7) {// 处理大二维码
                                    byte[] imageValue = cellDataList.get(0).getImageValue();
                                    // 需要先判断大二维码值存在的时候才进行转化
                                    if (imageValue != null) {
                                        // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上
                                        int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG);
                                        Drawing drawing = sheet.getDrawingPatriarch();
                                        // 如果画布为空就创建一个
                                        if (drawing == null) {
                                            drawing = sheet.createDrawingPatriarch();
                                        }
                                        CreationHelper helper = sheet.getWorkbook().getCreationHelper();
                                        ClientAnchor anchor = helper.createClientAnchor();
                                        // 设置图片坐标
                                        anchor.setDx1(0);
                                        anchor.setDx2(0);
                                        anchor.setDy1(0);
                                        anchor.setDy2(0);
                                        //设置图片位置
                                        anchor.setCol1(cell.getColumnIndex());
                                        anchor.setCol2(cell.getColumnIndex() + 2);
                                        anchor.setRow1(cell.getRowIndex());
                                        anchor.setRow2(cell.getRowIndex() + 3);
                                        // 设置图片可以随着单元格移动
                                        anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
//                                    drawing.createPicture(anchor, 0);
                                        drawing.createPicture(anchor, index);
                                    }
                                }
                            }
                        })
                .withTemplate(projectPath).build();

完整代码

package com.easyexcelexport.server.Impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.CellDataTypeEnum;
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.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
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.style.HorizontalCellStyleStrategy;
import com.easyexcelexport.entity.EasyexcelExportModel;
import com.easyexcelexport.server.EasyexcelExportService;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class EasyexcelExportServiceImpl implements EasyexcelExportService {
    /**
     * 根据模板导出excel
     */
    @Override
    public void exportExcelByTemplate(HttpServletResponse response) throws IOException {
        // 定义需要导出的 实体list
        List<EasyexcelExportModel> easyexcelExportModels = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            EasyexcelExportModel model = new EasyexcelExportModel();
            model.setId(i);
            model.setName("name" + i);
            model.setAge(i*10);
            // 添加二维码,把二维码转为byte[]
            byte[] qrCode = QrCodeUtil.generatePng(i+"1",
                    QrConfig.create().setCharset(Charset.forName("utf-8")));
            model.setQrCode(qrCode);
            easyexcelExportModels.add(model);
        }
        // 添加需要导出的特殊字段值
        Map<String,Object> map = new HashMap<>();
        map.put("operator","操作员007");
        map.put("Signer","签收员009");
        map.put("bigQrCode",QrCodeUtil.generatePng("bigQrCode",
                QrConfig.create().setCharset(Charset.forName("utf-8"))));

        log.info("=====easyexcel处理开始==========");
        InputStream projectPath =  this.getClass().getClassLoader().getResourceAsStream("excelTemplate"+ File.separator+ "exportExcelByTemplate.xlsx");
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        //防止中文乱码
        response.setHeader("Content-disposition", "attachment;filename=" + System.currentTimeMillis() + ".xlsx");


        // 这里 需要指定写用哪个class去写
        ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
                .registerWriteHandler(
                        new 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 afterCellDataConverted(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
                                // 因为导出的 二维码图片都在第 4列,这里先将图片列设置为空;因为如果这里不设置为空的话,
                                // 后面再根据这个拉伸的话,这小二维码还会存在
                                int columnIndex = cell.getColumnIndex();
                                byte[] imageValue = cellData.getImageValue();
                                if ((3==columnIndex || 7 == columnIndex) && imageValue != null) {
                                    cellData.setType(CellDataTypeEnum.EMPTY);
                                }
                            }
                            // 在单元上的所有操作完成后调用
                            @Override
                            public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {

                                // 设置样式
                                // 获取当前sheet
                                Sheet sheet = cell.getSheet();
                                int columnIndex = cell.getColumnIndex();
                                if (columnIndex == 3) {
                                    // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上
                                    int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG);
                                    Drawing drawing = sheet.getDrawingPatriarch();
                                    // 如果画布为空就创建一个
                                    if (drawing == null) {
                                        drawing = sheet.createDrawingPatriarch();
                                    }
                                    CreationHelper helper = sheet.getWorkbook().getCreationHelper();
                                    ClientAnchor anchor = helper.createClientAnchor();
                                    // 设置图片坐标
                                    anchor.setDx1(0);
                                    anchor.setDx2(0);
                                    anchor.setDy1(0);
                                    anchor.setDy2(0);
                                    //设置图片位置
                                    anchor.setCol1(cell.getColumnIndex());
                                    anchor.setCol2(cell.getColumnIndex() + 2);
                                    anchor.setRow1(cell.getRowIndex());
                                    anchor.setRow2(cell.getRowIndex()+1);
                                    // 设置图片可以随着单元格移动
                                    anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
//                                    drawing.createPicture(anchor, 0);
                                    drawing.createPicture(anchor,index);
                                } else if (columnIndex == 7) {// 处理大二维码
                                    byte[] imageValue = cellDataList.get(0).getImageValue();
                                    // 需要先判断大二维码值存在的时候才进行转化
                                    if (imageValue != null) {
                                        // 这里一定要有,这就是假设在画布上画上 上面设置为空的二维码,并返回其索引;在后面真正的拉伸之后的图片还是放在这个索引上
                                        int index = sheet.getWorkbook().addPicture(cellDataList.get(0).getImageValue(), HSSFWorkbook.PICTURE_TYPE_PNG);
                                        Drawing drawing = sheet.getDrawingPatriarch();
                                        // 如果画布为空就创建一个
                                        if (drawing == null) {
                                            drawing = sheet.createDrawingPatriarch();
                                        }
                                        CreationHelper helper = sheet.getWorkbook().getCreationHelper();
                                        ClientAnchor anchor = helper.createClientAnchor();
                                        // 设置图片坐标
                                        anchor.setDx1(0);
                                        anchor.setDx2(0);
                                        anchor.setDy1(0);
                                        anchor.setDy2(0);
                                        //设置图片位置
                                        anchor.setCol1(cell.getColumnIndex());
                                        anchor.setCol2(cell.getColumnIndex() + 2);
                                        anchor.setRow1(cell.getRowIndex());
                                        anchor.setRow2(cell.getRowIndex() + 3);
                                        // 设置图片可以随着单元格移动
                                        anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
//                                    drawing.createPicture(anchor, 0);
                                        drawing.createPicture(anchor, index);
                                    }
                                }
                            }
                        })
                .withTemplate(projectPath).build();
        // 这里注意 如果同一个sheet只要创建一次
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        if (CollectionUtil.isNotEmpty(map)){
            // 将特殊字段的值赋值
            excelWriter.fill(map,writeSheet);
        }
        // 这里注意 入参用了forceNewRow 代表在写入list的时候不管list下面有没有空行 都会创建一行,然后下面的数据往后移动。默认 是false,会直接使用下一行,如果没有则创建。
        // forceNewRow 如果设置了true,有个缺点 就是他会把所有的数据都放到内存了,所以慎用
        // 简单的说 如果你的模板有list,且list不是最后一行,下面还有数据需要填充 就必须设置
        //forceNewRow=true 但是这个就会把所有数据放到内存 会很耗内存
        // 如果数据量大 list不是最后一行 参照下一个
        FillConfig fillConfig =
                FillConfig.builder().forceNewRow(Boolean.TRUE).build();
        excelWriter.fill(easyexcelExportModels,fillConfig,writeSheet);
        log.info("=====easyexcel处理结束=========");
        // 关闭流
        projectPath.close();
        if (excelWriter != null) {
            excelWriter.finish();
        }
    }
}

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐