背景
最近有一个需求是需要动态导出合同、订单等信息,导出一个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-tl 1.10.0 org.apache.poi poi 4.1.2 org.apache.poi poi-ooxml 4.1.2 org.apache.poi poi-ooxml-schemas 4.1.2 commons-io commons-io 2.7
快速开始
流程:制作模板->提供数据->渲染模板->下载word
注意:需要填充的数据需要使用{{}}来表示。
1. 导出固定内容和图片的word文档
准备模板
模板保存为docx格式,存放在resource目录下
提供数据
private Map assertMap() { 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; }
工具方法
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(); } }
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.组装数据 Map params = 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 List<Student> studentList; private List<Student> studentList1; public List<Student> getStudentList1() { return studentList1; } public void setStudentList1(List<Student> studentList1) { this.studentList1 = studentList1; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public List<Student> getStudentList() { return studentList; } public void setStudentList(List<Student> studentList) { this.studentList = studentList; }}
表格数据
private StudentTable assertData() { StudentTable table = new StudentTable(); table.setTitle("我是标题"); List<Student> studentList = 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
效果如下:
来源地址:https://blog.csdn.net/qq_37758497/article/details/128858175