环境:jdk1.8+springboot2.5.6+easypoi

EasyPoi官网:1. 前言 - Powered by MinDoc (wupaas.com),easypoi的easypoi-spring-boot-starter坐标,这里建议用4.4.0版本,使用官网中的4.0.0需要在yml中额外的配置。这里就使用4.4.0版本了

 <dependency>
       <groupId>cn.afterturn</groupId>
       <artifactId>easypoi-spring-boot-starter</artifactId>
       <version>4.4.0</version>
  </dependency>

easypoi中的模板指令有

  • 空格分割

  • 三目运算 {{test ? obj:obj2}}

  • n: 表示 这个cell是数值类型 {{n:}}

  • le: 代表长度{{le:()}} 在if/else 运用{{le:() > 8 ? obj1 : obj2}}

  • fd: 格式化时间 {{fd:(obj;yyyy-MM-dd)}}

  • fn: 格式化数字 {{fn:(obj;###.00)}}

  • fe: 遍历数据,创建row

  • !fe: 遍历数据不创建row

  • $fe: 下移插入,把当前行,下面的行全部下移.size()行,然后插入

  • #fe: 横向遍历

  • v_fe: 横向遍历值

  • !if: 删除当前列 {{!if:(test)}}

  • 单引号表示常量值 ‘’ 比如’1’ 那么输出的就是 1

  • &NULL& 空格

  • &INDEX& 表示循环中的序号,自动添加

  • ]] 换行符 多行遍历导出

  • sum: 统计数据

  • cal: 基础的+-X% 计算

  • dict: 字典

  • i18n: 国际化

代码中的word模板和图片位置截图如下

在springboot项目中,在static目录下的图片和视频文件都是可以通过url地址访问的,如果文件在其他位置,就需要我们自己进行静态资源映射的配置,具体看(2条消息) springboot静态资源映射配置_m0_62317155的博客-CSDN博客_springboot静态资源映射配置

word模板截图如下所示

这里使用word模板的形式,word模板如下图所示,使用{{}}在word中填写对应的map字段即可,如{{name}}。注意是英文状态下的输入法。这里的文字大小和颜色、字体等可以使用word和wps软件先在模板中设置好。

注意上图中的{{$fe: studentList t.name}}这里必须放在表格中,我试过了如果没有放在表格中代码会报错不生效,如果担心这里表格影响,可以使用word软件或者wps将word模板中的表格线去掉即可

https://plus.wps.cn/blog/p35884.html

Java代码

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>wordexport</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>wordexport</name>
    <description>wordexport</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-spring-boot-starter</artifactId>
            <version>4.4.0</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>
Controller
package com.example.controller;

import cn.afterturn.easypoi.entity.ImageEntity;
import cn.afterturn.easypoi.word.WordExportUtil;
import com.example.entity.Student;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/export")
public class ExportWordController {

    @GetMapping("/exportword/{id}")
    public void exportword(@PathVariable Integer id, HttpServletResponse response) throws Exception {
        // 这里可以通过id查找数据库中保存的信息,这里就不查数据库了,大概数据数据测试就行了
        Student student = new Student();
        student.setId(id);
        student.setName("罗辑");
        student.setSex("男");
        student.setNation("汉族");
        student.setDescribe("我没有太多可说的,只有一个警告:生命从海洋登上陆地是地球生物进化的一个里程碑," +
                "但那些上岸的鱼再也不是鱼了;同样,真正进入太空的人,再也不是人了,所以,人们,当你们打算飞向外太空再也不回头时," +
                "请千万慎重,需付出的代价比你们想象的要大得多。" +
                "我们都是阴沟里的虫子,但总还是得有人仰望星空。" +
                "死亡是一座永恒的灯塔,不管你驶向何方,最终都会朝它转向。一切都将逝去,只有死神永生。");
        student.setResource("翻阅坐标数据是歌者的工作,判断坐标的诚意是歌者的乐趣。");
        // 读取word文档模板
        // File filepath = new File("D:\\private\\javastudaykeshanchu\\javaweb\\wordexport\\src\\main\\resources\\templates\\exportword_template.docx");
        File rootFile = new File((ResourceUtils.getURL("classpath:").getPath()));
        File templateFile = new File(rootFile, "/templates/exportword_template.docx");
        System.out.println(templateFile);
        // 需要将bean转换为map集合
        Map<String, Object> maps = new HashMap<>();
        maps.put("name", student.getName());
        maps.put("sex", student.getSex());
        maps.put("nation", student.getNation());
        maps.put("describe", student.getDescribe());
        maps.put("resource", student.getResource());

        // 写入图片
        ImageEntity imageEntity = new ImageEntity();
        // 这里可以是磁盘地址,也可以是对应的http地址,例如在springboot中static下的图片可以直接通过http的url访问。
        imageEntity.setUrl("http://localhost:8080/img/1.jpg");
        // 这里的宽高必须要设置
        imageEntity.setWidth(500);
        imageEntity.setHeight(300);
        maps.put("photo",imageEntity);

        ImageEntity imageEntity1 = new ImageEntity();
        imageEntity1.setUrl("D:\\private\\javastudaykeshanchu\\javaweb\\wordexport\\src\\main\\resources\\static\\img\\2.jpg");
        imageEntity1.setWidth(500);
        imageEntity1.setHeight(300);
        maps.put("photo1",imageEntity1);

        // 这里以集合List的形式,在word模板中使用模板指令fe:遍历数据,创建row
        List<Map<String,Object>> infoList = new ArrayList<>();
        Map<String,Object> mapObj = null;
        for (int i = 1; i <= 3; i++) {
            mapObj = new HashMap<>();
            ImageEntity imageEntity2 = new ImageEntity();
            // 这里可以是磁盘地址,也可以是对应的http地址,例如在springboot中static下的图片可以直接通过http的url访问。
            imageEntity2.setUrl("http://localhost:8080/img/" + i + ".jpg");
            // 这里的宽高必须要设置
            imageEntity2.setWidth(300);
            imageEntity2.setHeight(200);
            mapObj.put("imgurl",imageEntity2);
            mapObj.put("name", "罗辑" + i);
            infoList.add(mapObj);
        }
        maps.put("studentList", infoList);
        String name = student.getName();
        XWPFDocument word = WordExportUtil.exportWord07(templateFile.getPath(), maps);
        String fileName = "三体资料(" + name + ")详情.docx";
        response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes(), "ISO8859-1"));
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        ServletOutputStream outputStream = response.getOutputStream();
        word.write(outputStream);
        outputStream.close();
        word.close();
    }
}
Entity
package com.example.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private Integer id;
    private String name;
    private String sex;
    private String nation;
    private String describe;
    private String resource;
}

运行结果

在浏览器地址栏输入大概如下的地址http://localhost:8080/export/exportword/1,地址中的id为1是我随便输入的,这里之所以放id是因为在真实的开发中我们可以根据传入的id到对应的数据库表中查找到数据。我在代码中是自己懒得连数据库了,自己造了一个数据。具体看代码就行了。在浏览器输入的地址栏输入http://localhost:8080/export/exportword/1,即可下载一个word文档。将这个word文档打开之后得到如下的结果

代码中对应的字段和模板中对应的字段如下

最终的结果如下

跟前端代码联动

可以通过 window.location.href = "http://localhost:8080/export/exportword/1",调用对应的地址来实现导出word的功能。

使用window.location.href=url,window.open(url)也可以实现文件下载,但下载需要传请求头时,就不适用这种方法了。

参考文章:

(2条消息) vue前端调后台接口下载文件(get,post方法集合)爱写程序的小高的博客-CSDN博客vue调用下载文件接口

(2条消息) 下载Content-Type,Blob type类型汇总_爱写程序的小高的博客-CSDN博客

js下载doxc 文件示例和部分后缀对应的content-type 总结 - 天高任鸟飞吧 - 博客园 (cnblogs.com)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>vue简单页面试验</title>
    <!-- 生产环境版本,优化了尺寸和速度 -->
    <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
    <!-- 引入组件库 -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <!-- axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
</head>

<body>
    <div id="app">
        <el-button type="primary" @click="daochuWord">导出为word</el-button>
        <el-button type="primary" @click="daochuWord2">导出为word2</el-button>
        <el-button type="primary" @click="daochuWord3">导出为word3</el-button>
        <el-button type="primary" @click="daochuWord4">导出为word4</el-button>
    </div>
    <script>
        var app = new Vue({
            el: '#app',
            data: {

            },
            methods: {
                daochuWord() {
                    window.location.href = "http://localhost:8080/export/exportword/1"
                },
                daochuWord2() {
                    // 这个会闪一下就会关闭,暂时还在找办法解决中
                    window.open("http://localhost:8080/export/exportword/1")
                },
                // 这里使用了axios之后要注意了,注意看一下有没有配置axios的相应拦截器,这里因为是在线引入,所以没有配置响应拦截器。如果配置了响应拦截器,就要注意响应拦截器中是否是返回接口解构了axios的data的结果,这个一定要注意了。
                daochuWord3() {
                    axios.get('http://localhost:8080/export/exportword/1', { responseType: "blob" }).then(res => {
                        debugger
                        const link = document.createElement("a");
                        let blob = new Blob([res.data], { type: "application/msword" });
                        link.style.display = "none";
                        link.href = URL.createObjectURL(blob);
                        link.setAttribute("download", '三体资料(罗辑)详情.docx');
                        document.body.appendChild(link);
                        link.click();
                        document.body.removeChild(link);
                        this.$message.success("导出成功")
                    })
                },
                async daochuWord4() {
                    const res = await axios.get('http://localhost:8080/export/exportword/1', { responseType: "blob" })
                    const link = document.createElement("a");
                    let blob = new Blob([res.data], { type: "application/msword" });
                    link.style.display = "none";
                    link.href = URL.createObjectURL(blob);
                    link.setAttribute("download", '三体资料(罗辑)详情.docx');
                    document.body.appendChild(link);
                    link.click();
                    document.body.removeChild(link);
                    this.$message.success("导出成功")
                }
            }
        })
    </script>
    <style>
    </style>
</body>

</html>

对应的html页面如下,点击对应的按钮就可以实现导出word文档了

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐