三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

别再手动复制粘贴了!用poi-tl + Java搞定Word领料单自动生成(附完整源码)

别再手动复制粘贴了!用poi-tl + Java搞定Word领料单自动生成(附完整源码)

基于poi-tl的Word领料单自动化生成实战指南

在制造业和仓储管理领域,领料单作为物料流转的核心凭证,其生成效率直接影响着业务流程的顺畅度。传统手工制作方式不仅耗时费力,还容易因人为因素导致格式不统一、数据错误等问题。本文将深入探讨如何利用Java生态中的poi-tl库,实现领料单的自动化生成,彻底告别复制粘贴的低效工作模式。

1. 技术选型与环境搭建

1.1 poi-tl的核心优势

poi-tl(POI Template Lite)是基于Apache POI的Word模板引擎,相比原生POI API,它具有三大显著优势:

  1. 模板驱动开发:通过预定义的Word模板控制文档样式,代码仅关注数据绑定
  2. 丰富的标签体系:支持文本、图片、表格、列表等多种元素的动态渲染
  3. 高性能处理:优化了大文档生成的性能瓶颈,实测万行级表格生成仅需2-3秒

1.2 项目依赖配置

在pom.xml中添加以下依赖(版本号建议使用最新稳定版):

<dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.1</version> </dependency> <!-- POI基础依赖 --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>5.2.3</version> </dependency>

注意:poi-tl 1.12.x要求POI版本≥5.2.2,版本不匹配会导致兼容性问题

2. 模板设计与制作技巧

2.1 模板结构规划

领料单通常包含以下核心模块:

  • 页眉区:公司Logo、单据标题、流水号等固定信息
  • 基本信息区:领料部门、项目编号、日期等业务属性
  • 物料明细表:动态生成的物料列表,需支持分页
  • 页脚区:审批签名、二维码等辅助信息

2.2 关键标签使用

在Word模板中使用poi-tl的Mustache风格标签:

{{#list}} <!-- 区块循环开始 --> 基本信息表头内容... {{#tables}} <!-- 表格行循环 --> | 物料编码 | 物料名称 | 规格型号 | 单位 | 申请数量 | | {{code}} | {{name}} | {{spec}} | {{unit}} | {{quantity}} | {{/tables}} {{?isPageBreak}}分页标记{{/isPageBreak}} <!-- 分页控制 --> {{bottomWord}} <!-- 底部签名 --> {{/list}}

区块循环({{#list}})实现每页独立渲染,配合分页标记实现自动分页效果。表格行循环({{#tables}})则用于动态生成物料明细行。

3. 核心代码实现解析

3.1 数据准备与分页计算

// 计算总页数(每页30行) int pageSize = 30; int totalPages = (int) Math.ceil((double) materialList.size() / pageSize); List<Map<String, Object>> pageDataList = new ArrayList<>(); for (int page = 0; page < totalPages; page++) { Map<String, Object> pageData = new HashMap<>(); // 截取当前页数据 int fromIndex = page * pageSize; int toIndex = Math.min(fromIndex + pageSize, materialList.size()); List<MaterialItem> currentPageItems = materialList.subList(fromIndex, toIndex); // 填充模板变量 pageData.put("tables", currentPageItems); pageData.put("pageNum", page + 1); if (page < totalPages - 1) { pageData.put("isPageBreak", "分页标记"); } pageDataList.add(pageData); }

3.2 模板渲染与分页处理

// 加载模板文件 ClassPathResource templateResource = new ClassPathResource("templates/material_request.docx"); XWPFTemplate template = XWPFTemplate.compile(templateResource.getInputStream()) .render(Collections.singletonMap("list", pageDataList)); // 处理分页标记 template.getXWPFDocument().getParagraphs().forEach(paragraph -> { paragraph.getRuns().forEach(run -> { String text = run.getText(0); if (text != null && text.contains("分页标记")) { run.setText(text.replace("分页标记", ""), 0); run.addBreak(BreakType.PAGE); } }); }); // 输出到字节数组 ByteArrayOutputStream out = new ByteArrayOutputStream(); template.write(out); template.close();

4. 系统集成与性能优化

4.1 文件输出方案对比

输出方式适用场景实现复杂度性能影响
本地文件存储单机部署环境高IO开销
网络流直接响应实时下载需求内存友好
云存储服务上传分布式系统依赖网络

4.2 高频生成场景优化

当系统需要批量生成大量领料单时,可采用以下优化策略:

  1. 模板缓存:避免重复读取模板文件

    private static final XWPFTemplate CACHED_TEMPLATE; static { ClassPathResource resource = new ClassPathResource("templates/material_request.docx"); CACHED_TEMPLATE = XWPFTemplate.compile(resource.getInputStream()); }
  2. 异步生成队列:使用线程池处理生成请求

    ExecutorService executor = Executors.newFixedThreadPool(4); Future<byte[]> future = executor.submit(() -> generateDocument(data));
  3. 内存控制:对于超大文档,采用分块生成策略

5. 高级功能扩展

5.1 动态二维码集成

利用ZXing库生成包含领料单信息的二维码:

public static PictureRenderData generateQRCode(String content) throws WriterException { int size = 120; BitMatrix matrix = new QRCodeWriter().encode( content, BarcodeFormat.QR_CODE, size, size); BufferedImage image = MatrixToImageWriter.toBufferedImage(matrix); return Pictures.ofBufferedImage(image, PictureType.PNG) .size(size, size) .create(); }

5.2 模板热更新方案

实现不重启服务更新模板:

public byte[] generateWithLatestTemplate(RequestData data) throws IOException { Path templatePath = Paths.get(config.getTemplateDir(), "material_request.docx"); try (InputStream is = Files.newInputStream(templatePath)) { return XWPFTemplate.compile(is) .render(processData(data)) .writeToByteArray(); } }

在实际项目中,我们通过监听模板文件变更事件(如使用WatchService),实现了模板的实时热加载。当运营人员调整模板格式后,系统能在秒级内应用新模板,无需停机维护。

← 返回列表