SpringBoot动态导出word文档实整教程(复制即可使用)

 更新时间:2023年06月12日 08:51:00   作者:努力的蚂蚁【你若】  
在我们做项目的时候会需要把数据库中的数据导出到word当中,下面这篇文章主要给大家介绍了关于SpringBoot动态导出word文档实整教程的相关资料,文中的代码复制即可使用,需要的朋友可以参考下

背景

最近有一个需求是需要动态导出合同、订单等信息,导出一个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

<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>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.2</version>
        </dependency>

        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.7</version>
        </dependency>

快速开始

流程:制作模板->提供数据->渲染模板->下载word

注意:需要填充的数据需要使用{{}}来表示。

1. 导出固定内容和图片的word文档

准备模板

模板保存为docx格式,存放在resource目录下

提供数据

private Map<String, Object> assertMap() {
        Map<String, Object> 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.组装数据
        Map<String, Object> 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

效果如下:

总结 

到此这篇关于SpringBoot动态导出word文档实整教程的文章就介绍到这了,更多相关SpringBoot动态导出word文档内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot整合Elasticsearch实现索引和文档的操作方法

    SpringBoot整合Elasticsearch实现索引和文档的操作方法

    Elasticsearch 基于 Apache Lucene 构建,采用 Java 编写,并使用 Lucene 构建索引、提供搜索功能,本文分步骤通过综合案例给大家分享SpringBoot整合Elasticsearch的相关知识,感兴趣的朋友跟随小编一起看看吧
    2021-05-05
  • Spring如何使用三级缓存解决循环依赖

    Spring如何使用三级缓存解决循环依赖

    在Spring框架中,循环依赖是指两个或多个Bean相互依赖,形成闭环,导致无法完成初始化,此问题仅存在于单例Bean中,而原型Bean会抛出异常,Spring通过三级缓存及提前暴露策略解决循环依赖:一级缓存存放完全初始化的Bean
    2024-11-11
  • java httpclient设置超时时间和代理的方法

    java httpclient设置超时时间和代理的方法

    这篇文章主要介绍了java httpclient设置超时时间和代理的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-02-02
  • 深入浅出重构Mybatis与Spring集成的SqlSessionFactoryBean(上)

    深入浅出重构Mybatis与Spring集成的SqlSessionFactoryBean(上)

    通常来讲,重构是指不改变功能的情况下优化代码,但本文所说的重构也包括了添加功能。这篇文章主要介绍了重构Mybatis与Spring集成的SqlSessionFactoryBean(上)的相关资料,需要的朋友可以参考下
    2016-11-11
  • Java使用Jdbc连接Oracle执行简单查询操作示例

    Java使用Jdbc连接Oracle执行简单查询操作示例

    这篇文章主要介绍了Java使用Jdbc连接Oracle执行简单查询操作,结合实例形式详细分析了java基于jdbc实现Oracle数据库的连接与查询相关操作技巧,需要的朋友可以参考下
    2019-09-09
  • Java实现替换PDF中的字体功能

    Java实现替换PDF中的字体功能

    文档中可通过应用不同的字体来呈现不一样的视觉效果,通过字体来实现文档布局、排版等设计需要。本文将详细为大家介绍如何利用Java实现替换PDF文中的字体,需要的可以参考一下
    2022-03-03
  • Java基于二维数组实现的数独问题示例

    Java基于二维数组实现的数独问题示例

    这篇文章主要介绍了Java基于二维数组实现的数独问题,涉及java针对数组的遍历、计算、转换等相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • Java super和this的对比及使用

    Java super和this的对比及使用

    这篇文章主要介绍了Java super和this的对比及使用的相关资料,java中this与super会经常在使用的时候混淆,需要的朋友可以参考下
    2017-08-08
  • Java多线程批量数据导入的方法详解

    Java多线程批量数据导入的方法详解

    这篇文章主要介绍了Java多线程批量数据导入的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,下面小编和大家来一起学习下吧
    2019-06-06
  • springboot项目读取resources目录下的文件的9种方式

    springboot项目读取resources目录下的文件的9种方式

    本文主要介绍了springboot项目读取resources目录下的文件的9种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04

最新评论