背景
最近有一个需求是需要动态导出合同、订单等信息,导出一个word文档供客户进行下载查看。
需要导出的word文件,主要可以分为两种类型。
- 导出固定内容和图片的word文档
- 导出表格内容不固定的word文档
经过对比工具,我实践过两种实现方式。第一种是FreeMarker模板来进行填充;第二种就是文中介绍的POI-TL。
这里我推荐使用POI-TL。
介绍
POI-TL是word模板引擎,基于Apache POI,提供更友好的API。
目前最新的版本是1.12.X,POI对应版本是5.2.2。
这里需要注意的是POI和POI-TL有一个对应的关系。
准备工作
我使用的POI-TL版本是1.10.0
com.deepoove poi-tl1.10.0 org.apache.poi poi4.1.2 org.apache.poi poi-ooxml4.1.2 org.apache.poi poi-ooxml-schemas4.1.2 commons-io commons-io2.7
快速开始
流程:制作模板->提供数据->渲染模板->下载word
注意:需要填充的数据需要使用{{}}来表示。
1. 导出固定内容和图片的word文档
准备模板
模板保存为docx格式,存放在resource目录下
提供数据
private MapassertMap() { Map params = new HashMap<>(); params.put("name", "努力的蚂蚁"); params.put("age", "18"); params.put("image", Pictures.ofUrl("http://deepoove.com/images/icecream.png").size(100, 100).create()); return params; }
工具方法
/** * 将项目中的模板文件拷贝到根目录下 * @return */ private String copyTempFile(String templeFilePath) { InputStream inputStream = getClass().getClassLoader().getResourceAsStream(templeFilePath); String tempFileName = System.getProperty("user.home") + "/" + "1.docx"; File tempFile = new File(tempFileName); try { FileUtils.copyInputStreamToFile(inputStream, tempFile); } catch (IOException e) { throw new RuntimeException(e); } return tempFile.getPath(); }
private void down(HttpServletResponse response, String filePath, String realFileName) { String percentEncodedFileName = null; try { percentEncodedFileName = percentEncode(realFileName); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } StringBuilder contentDispositionValue = new StringBuilder(); contentDispositionValue.append("attachment; filename=").append(percentEncodedFileName).append(";").append("filename*=").append("utf-8''").append(percentEncodedFileName); response.addHeader("Access-Control-Allow-Origin", "*"); response.addHeader("Access-Control-Expose-Headers", "Content-Disposition,download-filename"); response.setHeader("Content-disposition", contentDispositionValue.toString()); response.setHeader("download-filename", percentEncodedFileName); try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(filePath)); // 输出流 BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream());) { byte[] buff = new byte[1024]; int len = 0; while ((len = bis.read(buff)) > 0) { bos.write(buff, 0, len); } } catch (Exception e) { e.printStackTrace(); } }
/** * 百分号编码工具方法 * @param s 需要百分号编码的字符串 * @return 百分号编码后的字符串 */ public static String percentEncode(String s) throws UnsupportedEncodingException { String encode = URLEncoder.encode(s, StandardCharsets.UTF_8.toString()); return encode.replaceAll("\+", "%20"); }
编写接口
@RequestMapping("genera") public void genera(HttpServletResponse response) { //1.组装数据 Mapparams = assertMap(); //2.获取根目录,创建模板文件 String path = copyTempFile("word/1.docx"); String fileName = System.currentTimeMillis() + ".docx"; String tmpPath = "D:\\" + fileName; try { //3.将模板文件写入到根目录 //4.编译模板,渲染数据 XWPFTemplate template = XWPFTemplate.compile(path).render(params); //5.写入到指定目录位置 FileOutputStream fos = new FileOutputStream(tmpPath); template.write(fos); fos.flush(); fos.close(); template.close(); //6.提供前端下载 down(response, tmpPath, fileName); } catch (Exception e) { e.printStackTrace(); } finally { //7.删除临时文件 File file = new File(tmpPath); file.delete(); File copyFile = new File(path); copyFile.delete(); } }
对于图片的格式,POI-TL也提供了几种方式来提供支撑。
测试
请求接口:http://127.0.0.1:1000/file/genera
效果如下:
2. 导出表格内容不固定的word文档
表格动态内容填充,POI-TL提供了3种方式。
- 表格行循环
- 表格列循环
- 动态表格。
第二种和第三种都可以实现表格填充,但我个人感觉第一种更方便一点,这里我只介绍【表格行循环】实现方式。
LoopRowTableRenderPolicy 是一个特定场景的插件,根据集合数据循环表格行。
注意:
- 模板中有两个list,这两个list需要置于循环行的上一行。
- 循环行设置要循环的标签和内容,注意此时的标签应该使用[]
准备模板
提供数据
学生实体类
public class Student { private String name; private String age; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
学生word类
public class StudentTable { private String title; private ListstudentList; private List studentList1; public List getStudentList1() { return studentList1; } public void setStudentList1(List studentList1) { this.studentList1 = studentList1; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public List getStudentList() { return studentList; } public void setStudentList(List studentList) { this.studentList = studentList; } }
表格数据
private StudentTable assertData() { StudentTable table = new StudentTable(); table.setTitle("我是标题"); ListstudentList = new ArrayList<>(); Student student = new Student(); student.setName("张三"); student.setAge("18"); studentList.add(student); Student student1 = new Student(); student1.setName("李四"); student1.setAge("20"); studentList.add(student1); Student student2 = new Student(); student2.setName("王五"); student2.setAge("21"); studentList.add(student2); Student student3 = new Student(); student3.setName("马六"); student3.setAge("19"); studentList.add(student3); table.setStudentList(studentList); table.setStudentList1(studentList); return table; }
编写接口
@RequestMapping("dynamicTable") public void dynamicTable(HttpServletResponse response) { //1.组装数据 StudentTable table = assertData(); //2.获取根目录,创建模板文件 String path = copyTempFile("word/2.docx"); //3.获取临时文件 String fileName = System.currentTimeMillis() + ".docx"; String tmpPath = "D:\\" + fileName; try { //4.编译模板,渲染数据 LoopRowTableRenderPolicy hackLoopTableRenderPolicy = new LoopRowTableRenderPolicy(); Configure config = Configure.builder().bind("studentList", hackLoopTableRenderPolicy).bind("studentList1", hackLoopTableRenderPolicy).build(); XWPFTemplate template = XWPFTemplate.compile(path, config).render(table); //5.写入到指定目录位置 FileOutputStream fos = new FileOutputStream(tmpPath); template.write(fos); fos.flush(); fos.close(); template.close(); //6.提供下载 down(response, tmpPath, fileName); } catch (Exception e) { e.printStackTrace(); } finally { //7.删除临时文件 File file = new File(tmpPath); file.delete(); File copyFile = new File(path); copyFile.delete(); } }
测试
请求接口:http://127.0.0.1:1000/file/dynamicTable
效果如下:
猜你喜欢
- 11天前(兰州旅游文化产业发展有限公司)甘肃省兰州市2023年乡村旅游暨A级旅游景区管理工作培训班开班
- 11天前(七尚酒店百度百科)Lohkah七尚酒店首度开创充满新知的闽地研学旅程
- 11天前(天气预报 华为)2025HDC华为天气上新系统级天气智能体,引领更智能的气象服务
- 11天前(甘肃文化旅游宣传片)甘肃文旅推介走进重庆
- 11天前(澳涞坞是什么)从本土品牌到全球舞台:澳涞山庄获国际顶级产业资源加持
- 11天前(中国旅游集团旗下酒店)中国旅游集团酒店控股有限公司战略投资雅阁酒店集团
- 11天前(希尔顿集团2021年筹建的酒店)希尔顿集团两大重点项目亮相第四届上海旅游投资促进大会
- 11天前(岭南东方大酒店)粤西成势 | 阳江阳春长兴岭南东方酒店正式签约,粤西文旅再添明珠
- 11天前(星级饭店的发展困境)星级饭店转型之路:从市场逻辑到行业实践的深度探索
- 11天前(新西兰登陆《我的世界》!全球首个目的地游戏模组震撼上线)新西兰登陆《我的世界》!全球首个目的地游戏模组震撼上线
网友评论
- 搜索
- 最新文章
- (2020广州车展哈弗)你的猛龙 独一无二 哈弗猛龙广州车展闪耀登场
- (哈弗新能源suv2019款)智能科技颠覆出行体验 哈弗重塑新能源越野SUV价值认知
- (2021款全新哈弗h5自动四驱报价)新哈弗H5再赴保障之旅,无惧冰雪护航哈弗全民电四驱挑战赛
- (海南航空现况怎样)用一场直播找到市场扩张新渠道,海南航空做对了什么?
- (visa jcb 日本)优惠面面俱到 JCB信用卡邀您畅玩日本冰雪季
- (第三届“堡里有年味·回村过大年”民俗花灯会活动)第三届“堡里有年味·回村过大年”民俗花灯会活动
- (展示非遗魅力 长安启源助力铜梁龙舞出征)展示非遗魅力 长安启源助力铜梁龙舞出征
- (阿斯塔纳航空公司)阿斯塔纳航空机队飞机数量增至50架
- (北京香港航班动态查询)香港快运航空北京大兴新航线今日首航
- (我在港航“呵护”飞机 每一次安全着陆就是最好的荣誉)我在港航“呵护”飞机 每一次安全着陆就是最好的荣誉
- 热门文章