赞
踩
说在前面的话
这两天有个以前的没碰过的项目提出来一个需求,导出word表格,并且是循环表格,注意是循环表格,而不是循环数据,当然既然是循环表格,顺带的数据肯定也要循环,还要带图片。先看看需求图(这是我自己参照需求画的,格式和需求一样)
模版
正文
我们用poi-tl:poi-tl是一个基于Apache POI的Word模板引擎,同时它也是一个免费开源(github地址)的Java类库,给Java程序员带来了word处理上的便捷。所需环境:
1. pom.xml 文件
<!--word导出相关依赖--> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.10.0</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>
2. pom.xml配置java代码获取resource下面word模版文件(很多博客都没看到这个配置)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
<!-- 过滤后缀文件 -->
<nonFilteredFileExtensions>
<!--<nonFilteredFileExtension>xlsx</nonFilteredFileExtension>
<nonFilteredFileExtension>xls</nonFilteredFileExtension>-->
<nonFilteredFileExtension>docx</nonFilteredFileExtension>
</nonFilteredFileExtensions>
</configuration>
</plugin>
3.java 代码测试类(当前测试类,可以复制到本地直接运行,前提是上面的pom.xml已经配置好,哦,还有图片地址换成自己本地的)
import com.deepoove.poi.XWPFTemplate; import com.deepoove.poi.data.*; import org.jeecg.common.util.DateUtils; import org.jeecg.modules.jointLogisticSupport.controller.JointLogisticSupportController; import org.jeecg.modules.jointLogisticSupport.entity.JointLogisticSupport; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class ExportTest { /** * 导出文件具体业务 * @throws Exception */ public static void main(String[] args) throws Exception { // 模版放在resource String filePath = "/jointTemplate.docx"; // 获取模版文件流 InputStream inputStream = JointLogisticSupportController.class.getResourceAsStream(filePath); if (inputStream == null) { throw new IllegalArgumentException("Template file not found: " + filePath); } // 表格头 RowRenderData tableHead = Rows.of("编号", "规格", "位置","状态").center().bgColor("4472c4").create(); // 数据列表 List<JointLogisticSupport> pageList = new ArrayList<>(5); // 创建表格list List<RowRenderData> rowDataList = new ArrayList<>(); List<RowRenderData> rowDataList1 = new ArrayList<>(); Map<String,Object> map = new HashMap<>(); // 循环创建每一行表格 int order = 1; for (int i = 0; i < 5; i++) { // 这里我就直接循环了,查出来的list数据自行循环 rowDataList.add(Rows.create(order+"", "规格"+order, "位置"+order,"正常")); order ++; // 第二个表格 rowDataList1.add(Rows.create("产品名称","产品"+order,"描述","描述","状态","正常")); rowDataList1.add(Rows.create("检验是否合格","是","检验日期","yyyy-mm-dd","检验人","程序猿")); // 图片路径 String east = null; String prospectMap = null; // 因为创建表格时不能放入图片,所以先生成一个图片占位符放在临时文件中 String eastPicture = null; String prospectMapPicture = null; // 把word中的占位符对应的值存入map String eastPic = null; String prospectMapPic = null; // 把占位符和图片路径放在map中 /** * 下面注释部分是自己项目的代码,现在把它注释掉 * 我们写测试用的代码 */ // if (StringUtils.isNotEmpty(item.getEast())){ // east =item.getEast(); // if(loadUrl(east)){ // eastPicture = "{{@eastPicture"+order+"}}"; // eastPic = "eastPicture"+order; // map.put(eastPic, Pictures.ofUrl(east).size(86, 86).create()); // } // } // if (StringUtils.isNotEmpty(item.getProspectMap())){ // prospectMap =item.getProspectMap(); // if(loadUrl(prospectMap)){ // prospectMapPicture = "{{@prospectMapPicture"+order+"}}"; // prospectMapPic = "prospectMapPicture"+order; // map.put(prospectMapPic,Pictures.ofLocal(prospectMap).size(86, 86).create()); // } // } // word 文档占位符 eastPicture = "{{@eastPicture"+order+"}}"; prospectMapPicture = "{{@prospectMapPicture"+order+"}}"; // java 代码对应占位符的key键 eastPic = "eastPicture"+order; prospectMapPic = "prospectMapPicture"+order; // 图片map数据 map.put(eastPic,Pictures.ofLocal("E:\\正面.jpg").size(86, 86).create()); map.put(prospectMapPic,Pictures.ofLocal("E:\\收割机侧面.jpg").size(86, 86).create()); rowDataList1.add(Rows.create("正面照",eastPicture,null,"侧面照",prospectMapPicture,null)); } // 合并规则,创建两个,第一个表格规则 MergeCellRule rule = MergeCellRule.builder().build(); // 第二个表格规则 MergeCellRule.MergeCellRuleBuilder mergeRuleBuilder = MergeCellRule.builder(); // 根据特定条件设置合并规则 for (int i = 0; i < rowDataList1.size(); i++) { // 在此处根据具体条件来判断是否要设置合并规则 if (i%3==2){ mergeRuleBuilder.map(MergeCellRule.Grid.of(i, 1), MergeCellRule.Grid.of(i, 2)).map(MergeCellRule.Grid.of(i, 4), MergeCellRule.Grid.of(i, 5)); } } MergeCellRule rule1 = mergeRuleBuilder.build(); // 第一次生成的临时文件 String prefix = String.valueOf(System.currentTimeMillis()); File tempFile = File.createTempFile(prefix, ".docx"); // 获取临时文件路径 String firstTargetPath = tempFile.getAbsolutePath(); // 根据模版填充临时文件 XWPFTemplate template = XWPFTemplate.compile(inputStream).render( new HashMap<String, Object>() { { // 生成第一个表格 TableRenderData table = Tables.of(tableHead).center().create(); // 逐行添加数据到表格 for (RowRenderData rowData : rowDataList) { table.addRow(rowData); } // 设置合并规则 table.setMergeRule(rule); put("table", table); // 生成第二个表格 TableRenderData table1 = Tables.create(); // 逐行添加数据到表格 for (RowRenderData rowData : rowDataList1) { table1.addRow(rowData); } // 设置合并规则 table1.setMergeRule(rule1); put("table1", table1); } }); template.writeAndClose(new FileOutputStream(firstTargetPath)); // 获取临时文件模版,并把图片存入表格中 XWPFTemplate templatePic = XWPFTemplate.compile(firstTargetPath).render(map); Long currentTime = DateUtils.getCurrentTimestamp(); String fileName = "信息采集_"+currentTime+".docx"; String path = "E:\\" + fileName; // 写入 templatePic.writeAndClose(new FileOutputStream(path)); // 删除创建的临时文件 deleteFile(firstTargetPath); //response.getOutputStream().write(fileName.getBytes()); } /** * 删除临时文件 * @param filePath */ private static void deleteFile(String filePath) { File file = new File(filePath); if (file.exists()) { file.delete(); } } /** * 访问图片是否存在 * @param imageUrl * @return */ private boolean loadUrl(String imageUrl){ try { // 创建URL对象 URL url = new URL(imageUrl); // 打开连接 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("HEAD"); // 获取响应状态码 int responseCode = connection.getResponseCode(); connection.disconnect(); if (responseCode == HttpURLConnection.HTTP_OK) { System.out.println("图片存在"); // 关闭连接 return true; } else { // 关闭连接 System.out.println("图片不存在"); return false; } } catch (IOException e) { System.out.println("发生异常:" + e.getMessage()); return false; } } }
执行效果
执行完之后会在电脑上生成一个word文档。
前端导出word文档
1.需要把main方法的最后一句打开
2. 前端vue代码,自动下载的
// 导出按钮点击事件方法 handleExportWord() { let param = this.getQueryParams(); if (this.selectedRowKeys && this.selectedRowKeys.length > 0) { param['selections'] = this.selectedRowKeys.join(","); } console.log("导出参数", param); // 改成自己项目封装好的get方法,请求方法改成自己的 getAction(this.url.exportWordUrl,{ param:param, // responseType:'blob' }).then(response => { console.log('test',response); if (!response) { return } const url = window._CONFIG['staticDomainURL']+'//'+response; const link = document.createElement('a'); link.style.display = 'none'; link.href = url; link.setAttribute('download', fileName + '.docx'); document.body.appendChild(link); link.click(); document.body.removeChild(link); window.URL.revokeObjectURL(url); }) .catch(error => { console.error(error); this.$message.warning("文件下载失败"); }); }
最后
这个放入图片的方法是一种比较笨的方法,但是能达到实现效果,我也查了很多博客,文档,但是没有看到循环表格时加入图片的,其他人如果有好的方法发出来,大家一起学习。
参考文档:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。