【工具集】【后端】【EasyExcel】动态Excel模版生成Excel

1.需求描述

  • 将数据动态保存到Excel中,其中数据包含文本文字、图片base64的格式
  • Excel中支持sheet之间的跳转、字体颜色的控制
  • sheet数量不固定,需要动态创建sheet模板

2.实现思路

  • 创建Excel模板
  • 组织数据
  • Excel模板的制作
  • 向制作出的Excel模板,填充数据

注意:excel生成的sheet,有数量上限,这个需要根据业务需求权衡控制

3.具体实现

实现框架以Spring Boot为基础建设的。

3.1.Excel模板

在这里插入图片描述
在这里插入图片描述

3.2.添加Maven依赖

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-json</artifactId>
    <version>5.5.7</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>
<dependency>
  <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.1.1</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.1.2</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.1.2</version>
</dependency>

3.3.Java 引用

import cn.hutool.core.io.resource.Resource;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.poi.excel.ExcelUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.data.HyperlinkData;
import com.alibaba.excel.metadata.data.RichTextStringData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.util.BooleanUtils;
import com.alibaba.excel.util.ListUtils;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillWrapper;
import com.alibaba.excel.write.metadata.style.WriteFont;

3.4.Java 实现

private static final String ReportPath = "C:\\XXX\\XXX\\xxx"; // excel模版文件根路径
private static final Resource templateFileObj = ResourceUtil.getResourceObj(ReportPath + "\\excelTemplate\\template.xlsx"); // excel模版文件名称

@Override
public String makeReport(ReportRespMsg data) throws IOException {

    if (data == null) {
        return null;
    }

    String rootPath = templateFileObj.getUrl().getPath();
    rootPath = rootPath.substring(0, rootPath.lastIndexOf("excelTemplate/template.xlsx"));

    System.out.println(templateFileObj.getUrl().getPath());

    String excelName = "report_" + System.currentTimeMillis();
    String fileName = rootPath + "excelWrite/"+ excelName + ".xlsx";
    String excelWriteDirPath = rootPath + "excelWrite";
    System.out.println(fileName);

    try {
        File excelWriteFileDir = new File(excelWriteDirPath);
        if (!excelWriteFileDir.exists()) {
            excelWriteFileDir.mkdirs();
        }
        writeExcel(rootPath, fileName, data);
        return excelName;
    } catch (Exception e) {
        log.error(e.getMessage());
    }
    return null;
}

/**
 * 制作Excel模版并写入数据.
 *
 * @param rootPath 文件根路径
 * @param fileName 生成的文件名称
 * @param data 填充到excel中的数据
 * @throws IOException
 */
private void writeExcel(String rootPath, String fileName, ReportRespMsg data) throws IOException {
    // 模版制作
    XSSFWorkbook workbook = new XSSFWorkbook(templateFileObj.getStream());
    if (data != null && data.getDetail() != null && data.getDetail().size() > 0) {
        for (int i = 1; i < data.getErrorDetail().size() && data.getDetail().size() <= data.getMaxSize(); i++) {
            int index = i+1;
            String sheetName = "sheet" + index;
            workbook.cloneSheet(1, sheetName);
        }
    } else {
        // 删除默认模版Sheet
        workbook.removeSheetAt(1);
    }
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    workbook.write(bos);
    InputStream is = new ByteArrayInputStream(bos.toByteArray());
    // 模版制作-结束

    // 写入数据
    ExcelWriter excelWriter = EasyExcel.write(fileName)
            .withTemplate(is)
            .build();

    WriteSheet writeSheet = EasyExcel.writerSheet(0,"Sheet0").build();

    excelWriter.fill(new FillWrapper("a", data.getA()), writeSheet);
    excelWriter.fill(new FillWrapper("b", data.getB()), writeSheet);
    excelWriter.fill(new FillWrapper("c", excelData(data.getDetail())), writeSheet);

    Map<String, Object> map = new HashMap<>(5);

    map.put("t1", data.getT1());
    map.put("t2", "");

    if (data.getT3s() != null) {
        map.put("t3s", data.getT3s().stream().collect(Collectors.joining(", ")));
    }

    excelWriter.fill(map, writeSheet);

    WriteFont CUSTOM_FONT = new WriteFont();
    CUSTOM_FONT.setFontName("微软雅黑");
    CUSTOM_FONT.setColor(IndexedColors.RED.getIndex());
    CUSTOM_FONT.setFontHeightInPoints((short) 12);

    // 写入数据
    for (int i = 0; i < data.getDetail().size() && i < data.getMaxSize(); i++) {
        int index = i+1;
        String sheetName = "sheet" + index;
        WriteSheet childSheet = EasyExcel.writerSheet(index, sheetName)
                .registerWriteHandler(new CellWriteHandler() {
                    @Override
                    public void afterCellDispose(CellWriteHandlerContext context) {
                        if (BooleanUtils.isNotTrue(context.getHead())) {
                            context.getFirstCellData().getOrCreateStyle().setWriteFont(CUSTOM_FONT);
                        }
                    }
                })
                .build();

        ExcelData item = data.getDetail().get(i);

        excelWriter.fill(new FillWrapper("d1", item.getContentTable().getD1()), childSheet);

        Map<String, Object> map1 = new HashMap<>(6);

        map1.put("id", item.getId());
        map1.put("m1", item.getContentTable().getM1());
        map1.put("m2", item.getContentTable().getM2());

        excelWriter.fill(map1, childSheet);
    }
    // 写入数据-结束

    excelWriter.finish();

    writeImg(fileName, data.getEcharts());
}

/**
 * 向excel写入图片(定制).
 *
 * @param filePath 生成的文件名称
 * @param data 填充到excel中的数据
 * @throws IOException
 */
private void writeImg(String filePath, Map<String, String> data) throws IOException {

    cn.hutool.poi.excel.ExcelWriter writer = ExcelUtil.getWriter(filePath);
    Sheet sheet = writer.getSheets().get(0);
    Drawing drawingPatriarch = sheet.createDrawingPatriarch();

    BASE64Decoder decoder = new BASE64Decoder();
    Iterator<Map.Entry<String, String>> itemIterator = data.entrySet().iterator();

    // 默认第0个图 (图片定位,也可以用填充形式fill+变量)
    int dx1 = 0;
    int dy1 = 0;
    int dx2 = 0;
    int dy2 = 0;

    int[][] img = new int[][]{
            {7, 0, 13, 3},
            {15, 0, 24, 10},
            {27, 0, 36, 10}
    };

    int num = 0;
    while (itemIterator.hasNext()) {
        Map.Entry<String, String> entry = itemIterator.next();

        byte[] b = decoder.decodeBuffer(entry.getValue().replace("data:image/png;base64,", ""));
        for (int i = 0; i < b.length; ++i) {
            if (b[i] < 0) {
                b[i] += 256;
            }
        }

        // 设置图片单元格位置
        ClientAnchor anchor = drawingPatriarch.createAnchor(dx1, dy1, dx2, dy2, img[num][1], img[num][0], img[num][3], img[num][2]);

        anchor.setAnchorType(ClientAnchor.AnchorType.MOVE_AND_RESIZE);

        // 添加图片
        int pictureIndex = sheet.getWorkbook().addPicture(b, HSSFWorkbook.PICTURE_TYPE_PNG);
        drawingPatriarch.createPicture(anchor, pictureIndex);

        ++num;
    }

    writer.flush().close();
}

/**
 * excel中列表的样式(定制).
 * @param detail 详细信息
 * @return 详细excel信息
 */
private List<ExcelData> excelData(List<ExcelData> detail) {
    if (detail!= null && !detail.isEmpty()) {
        AtomicInteger index = new AtomicInteger(1);
        detail.forEach(item -> {
            WriteCellData<String> hyperlink = new WriteCellData<>();
            HyperlinkData hyperlinkData = new HyperlinkData();
            String sheetName = "#'sheet" + index + "'!A1";
            hyperlinkData.setAddress(sheetName);
            hyperlinkData.setHyperlinkType(HyperlinkData.HyperlinkType.DOCUMENT);
            hyperlink.setHyperlinkData(hyperlinkData);
            hyperlink.setType(CellDataTypeEnum.RICH_TEXT_STRING);
            RichTextStringData richTextStringData = new RichTextStringData();
            richTextStringData.setTextString("查看详细");
            WriteFont writeFont = new WriteFont();
            writeFont.setColor(IndexedColors.BLUE.getIndex());
            richTextStringData.applyFont(writeFont);
            hyperlink.setRichTextStringDataValue(richTextStringData);
            item.setDetail(hyperlink);
            index.incrementAndGet();
        });
    }

    return detail;
}

附录

参考

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值