文章目录
@[TOC](文章目录) 前言一、需要的依赖二、制作word模板1. 原word表单文件2. 制作word模板3. 最后通过代码生成的word数据表格 三、示例代码1.测试工具类2. 表单对象3. 表单行数据循环对象 四、参考或相关文档
前言
java项目实际开发中经常会遇到制作word表单且表格数据行循环功能,甚至带有复选框勾选功能,本文简单介绍如何制作模板以及使用poi-tl生成word。
提示:以下是本篇文章正文内容,下面案例可供参考
一、需要的依赖
如果只用到word那么需要导入的依赖如下(本案例只需要如下2个依赖生成word):
<dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.8.0.M2</version> </dependency> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.9.1</version> </dependency>
正常企业项目还有excel等其他office工具,所以需要导入其他依赖:
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifactId>ooxml-schemas</artifactId><version>1.4</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>
注意:一定要看清楚导入的poi-tl的版本,不同版本对应的jdk,Apache POI版本都不一样,且代码中需要调用的函数也可能会不一样。
poi-tl不同版本相关文档介绍
二、制作word模板
以下都是示例模板
1. 原word表单文件
2. 制作word模板
3. 最后通过代码生成的word数据表格
三、示例代码
1.测试工具类
import cn.hutool.core.bean.BeanUtil;import com.deepoove.poi.XWPFTemplate;import com.deepoove.poi.config.Configure;import com.deepoove.poi.config.ConfigureBuilder;import com.deepoove.poi.policy.HackLoopTableRenderPolicy;import com.deepoove.poi.util.PoitlIOUtils;import com.haiyang.Entity.DataForm;import com.haiyang.Entity.VoteInfo;import lombok.extern.slf4j.Slf4j;import org.junit.Test;import org.junit.runner.RunWith;import org.springframework.boot.test.context.SpringBootTest;import org.springframework.core.io.ClassPathResource;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import org.springframework.web.context.request.RequestContextHolder;import org.springframework.web.context.request.ServletRequestAttributes;import javax.servlet.http.HttpServletResponse;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import java.util.List;import java.util.Map;@SpringBootTest@RunWith(SpringJUnit4ClassRunner.class)@Slf4jpublic class GenerateWordTest { @Test public void generateWordForm() { /** * * 若是调用生成word的方法参数不能带response 可以通过request上下文获取 */ //获取response对象 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletResponse response = attributes.getResponse(); /** * 1. 获取所有投票人的列表数据 * 此处使用测试数据,实际项目中需要自己从数据库查询 * */ List<VoteInfo> infoList = new ArrayList<VoteInfo>() {{ add(new VoteInfo("张三", "1", "没有意见")); add(new VoteInfo("李四", "1", "同意")); add(new VoteInfo("王五", "3", "拒绝,不同意")); add(new VoteInfo("宋小宝", "4", "我回避!不做任何评价")); add(new VoteInfo("海燕", "2", "我认为需要续议,日后再重新讨论")); }}; StringBuffer allOpinion = new StringBuffer(); for (VoteInfo info : infoList) { //拼接所有投票人的意见 并换行 allOpinion.append(info.getVoterName() + ":" + info.getVoteOpinion() + "\n"); } /** * 2. 获取并设置表单数据 此处为测试数据 meetingType和passFlag默认设置为1 * 复选框 meetingType 会议类型 1、股东会 2、董事会 3、合伙人会 4、其他 * 复选框 passFlag 是否通过 1、同意 2、续议 3、拒绝 * */ DataForm dataForm = new DataForm("程序猿公司", "2024", "1", "1", "程序猿股东大会-如何走向人生巅峰迎娶白富美", "1", "1", allOpinion.toString(), "程序猿", new SimpleDateFormat("yyyy-MM-dd").format(new Date())); /** * 3. 数据map 用于渲染word表单的数据 将表单数据对象存入map中 */ Map<String, Object> dataMap = BeanUtil.beanToMap(dataForm); //需要循环的表单数据 dataMap.put("dataTable", infoList); /** * 4. Configure类是该库中的一个配置类,其作用是提供了一些全局的配置选项 * (1) useSpringEL() 开启El表达式{{ }} word模板中的数据就以这个表达式传递数据 例如:{{companyName}};也可以调用buildGramer("${", "}") 可以修改模板为${} * (2) bind() 绑定标记需要循环的数据 * (3) 实现表格行循环的策略 HackLoopTableRenderPolicy 不同poi版本实现行循环的策略不一样;当前案例是poi-tl 1.9.1版本 * 1.9.x版本:HackLoopTableRenderPolicy 1.10.x以后的版本:LoopRowTableRenderPolicy * */ ConfigureBuilder configureBuilder = Configure.builder().useSpringEL().bind("dataTable", new HackLoopTableRenderPolicy()); Configure config = configureBuilder.build(); InputStream is = null; try { /** * 5. word模板渲染数据 wordForm.docx为模板,本案例放在了项目根目录的wordTemplates下 */ is = new ClassPathResource("wordTemplates/wordForm.docx").getInputStream(); XWPFTemplate template = XWPFTemplate.compile(is, config).render(dataMap); /** 6. 根据模板生成word文件的指定路径 */ String filePath = "D:/note/程序猿会议意见反馈表.docx"; //生成 意见反馈表word 到指定路径 OutputStream out = new FileOutputStream(filePath); /** 若是直接访问下载打开 可以通过如下方式实现 不需要设置文件路径 * String fileName = URLEncodeUtil.encode("程序猿会议意见反馈表"); * response.setContentType("application/octet-stream;charset=utf-8"); * response.setHeader("Content-Disposition", "attachment;filename=" + fileName + ".docx"); * OutputStream out = response.getOutputStream(); */ /** * 7. 文件输出 */ template.writeAndClose(out); out.flush(); PoitlIOUtils.closeQuietlyMulti(template, out); } catch (IOException e) { log.error("生成意见反馈表失败!", e); } finally { if (null != is) { try { //最后别忘记了关闭流 is.close(); } catch (IOException e) { log.error("关闭流失败!", e); } } } }}
2. 表单对象
/** * word数据表单 */public class DataForm { /** 公司名称 */ private String companyName; /** 时间-年 */ private String year; /** 时间-月 */ private String month; /** 时间-日 */ private String day; /** 会议主题 */ private String meetingTheme; /** 会议类型 1、股东会 2、董事会 3、合伙人会 4、其他 */ private String meetingType; /** 是否通过 1、同意 2、续议 3、拒绝 */ private String passFlag; /** 汇总意见 所有投票人的意见汇总 */ private String allOpinion; /** 签名 */ private String sign; /** 签名日期 yyyy-MM-dd */ private String signDate; public DataForm() { } public DataForm(String companyName, String year, String month, String day, String meetingTheme, String meetingType, String passFlag, String allOpinion, String sign, String signDate) { this.companyName = companyName; this.year = year; this.month = month; this.day = day; this.meetingTheme = meetingTheme; this.meetingType = meetingType; this.passFlag = passFlag; this.allOpinion = allOpinion; this.sign = sign; this.signDate = signDate; } public String getCompanyName() { return companyName; } public void setCompanyName(String companyName) { this.companyName = companyName; } public String getYear() { return year; } public void setYear(String year) { this.year = year; } public String getMonth() { return month; } public void setMonth(String month) { this.month = month; } public String getDay() { return day; } public void setDay(String day) { this.day = day; } public String getMeetingTheme() { return meetingTheme; } public void setMeetingTheme(String meetingTheme) { this.meetingTheme = meetingTheme; } public String getMeetingType() { return meetingType; } public void setMeetingType(String meetingType) { this.meetingType = meetingType; } public String getPassFlag() { return passFlag; } public void setPassFlag(String passFlag) { this.passFlag = passFlag; } public String getAllOpinion() { return allOpinion; } public void setAllOpinion(String allOpinion) { this.allOpinion = allOpinion; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public String getSignDate() { return signDate; } public void setSignDate(String signDate) { this.signDate = signDate; }}
3. 表单行数据循环对象
/** * 投票详情 */public class VoteInfo { /** 投票人名称 */ private String voterName; /** 投票结果 1、同意 2、续议 3、拒绝 4、回避 */ private String voteResult; /** 投票意见 */ private String voteOpinion; public VoteInfo() { } public VoteInfo(String voterName, String voteResult, String voteOpinion) { this.voterName = voterName; this.voteResult = voteResult; this.voteOpinion = voteOpinion; } public String getVoterName() { return voterName; } public void setVoterName(String voterName) { this.voterName = voterName; } public String getVoteResult() { return voteResult; } public void setVoteResult(String voteResult) { this.voteResult = voteResult; } public String getVoteOpinion() { return voteOpinion; } public void setVoteOpinion(String voteOpinion) { this.voteOpinion = voteOpinion; }}
四、参考或相关文档
poi-tl官方文档
poi-tl导出word, 含表格单元格合并,表格单元格多图合并
poi-tl导出复杂word/pdf文档