springboot+poi-tl根据模板导出word(含动态表格和图片),并将导出的文档压缩zip导出

springboot+poi-tl根据模板导出word(含动态表格和图片)

官网:http://deepoove.com/poi-tl/

参考网站:https://blog.csdn.net/M625387195/article/details/124855854

  • pom导入的maven依赖
java 复制代码
<dependency>
	<groupId>com.deepoove</groupId>
	<artifactId>poi-tl</artifactId>
	<version>1.12.1</version>
</dependency>
  • 准备模板

    文本标签用{``{ }},动态表格的字段标签用[]。

  • 代码实现

    3.1 控制器

    java 复制代码
    package io.renren.modules.sys.controller;
    import io.renren.common.utils.R;
    import io.renren.modules.sys.service.POIService;
    import org.springframework.beans.factory.annotation.Autowired;
    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;
    
    /**
     * @Author: Administrator
     * @Date: 2024/3/14
     * @Description:
     */
    @RestController
    @RequestMapping("/anli")
    public class AnliController {
    
        @Autowired
        private POIService poiService;
    
        @GetMapping("/daochu/{renwuId}")
        public R daochu(@PathVariable("renwuId") Long renwuId) {
            String zipUrl = poiService.anlidaochu(renwuId);
            return new R().put("zipUrl", zipUrl);
        }
    }

    3.2 实现类

    java 复制代码
    package io.renren.modules.sys.service;
    
    /**
     * @Author: Administrator
     * @Date: 2024/3/4
     * @Description:
     */
    public interface POIService {
        /**
         * 案例导出
         * @param renwuId
         */
        String anlidaochu(Long renwuId);
    }
    java 复制代码
    package io.renren.modules.sys.service.impl;
    	
    import io.renren.common.utils.word.WordUtils;
    import io.renren.modules.sys.dto.RenwuTemplateDTO;
    import io.renren.modules.sys.service.POIService;
    import io.renren.modules.sys.service.SysRenwuService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Service;
    
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.List;
    import java.util.UUID;
    import java.util.concurrent.CompletableFuture;
    import java.util.zip.ZipOutputStream;
    
    /**
     * @Author: Administrator
     * @Date: 2024/3/4
     * @Description:
     */
    @Service
    public class POIServiceImpl implements POIService {
        @Autowired
        private SysRenwuService renwuService;
        @Value("${upload.url}")
        private String UPLOAD_URL;
        @Value("${upload.path}")
        private String UPLOAD_SUFFIX_URL;
        public String getUPLOAD_URL() {
            return UPLOAD_URL + getUploadSuffixURL();
        }
        public String getUploadSuffixURL() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
            String dateString = sdf.format(new Date());
            return UPLOAD_SUFFIX_URL + dateString + "/";
        }
    
        /**
         * 案例导出
         * @param renwuId
         */
        @Override
        public String anlidaochu(Long renwuId) {
        	// 将要生成文档的数据查询出来
            RenwuTemplateDTO renwuTemplateDTO = renwuService.daochuByRenwuId(renwuId);
            String url = null;
            if (renwuTemplateDTO != null) {
                try {
                    List<String> urlList = WordUtils.piliangDaochu(renwuTemplateDTO);
                    if (urlList != null && urlList.size() > 0) {
                        String name = renwuTemplateDTO.getRenwuName()+"_"+ UUID.randomUUID() +".zip";
                        url =  this.getUploadSuffixURL() + name;
                        FileOutputStream fos = new FileOutputStream(this.getUPLOAD_URL() + name);
                        ZipOutputStream zos = new ZipOutputStream(fos);
                        for (String file : urlList) {
                            WordUtils.addToZipFile(file, zos);
                        }
                        zos.close();
                        fos.close();
                        // 使用异步线程删除文件
                        deleteFilesAsync(urlList);
                    }
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
            return url;
        }
    
        @Async
        public CompletableFuture<Void> deleteFilesAsync(List<String> urlList) {
            for (String file : urlList) {
                File fileToDelete = new File(file);
                if (fileToDelete.exists()) {
                    if (fileToDelete.delete()) {
                        System.out.println("Deleted file: " + file);
                    } else {
                        System.out.println("Failed to delete file: " + file);
                    }
                }
            }
            return CompletableFuture.completedFuture(null);
        }
    }

    3.3 配置文件

    yml 复制代码
    upload:
      url: H:/GoTionBackends/2023/resources
      path: /u/cms/www/
      outPath: H:/GoTionBackends/2023/resources/doc
      prefix: http://xxx.xxx.xxx:8087

    3.4 工具类

    java 复制代码
    package io.renren.common.utils.word;
    
    import com.alibaba.fastjson.JSON;
    import com.deepoove.poi.XWPFTemplate;
    import com.deepoove.poi.config.Configure;
    import com.deepoove.poi.data.*;
    import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
    import com.deepoove.poi.policy.PictureRenderPolicy;
    import io.renren.common.utils.word.dto.WordQingdanDetailsDTO;
    import io.renren.modules.sys.dto.RenwuTemplateDTO;
    import io.renren.modules.sys.entity.SysQingdanExtEntity;
    import org.apache.commons.lang.StringUtils;
    
    import java.io.*;
    import java.nio.file.Files;
    import java.nio.file.Paths;
    import java.text.SimpleDateFormat;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.UUID;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipOutputStream;
    
    /**
     * @Author: Administrator
     * @Date: 2024/3/1
     * @Description:
     */
    public class WordUtils {
        public static List<String> piliangDaochu(RenwuTemplateDTO renwuTemplate) throws IOException {
            List<String> urlList = new ArrayList<>();
            if (renwuTemplate.getQingdanDTOList() != null && renwuTemplate.getQingdanDTOList().size() > 0) {
                for (int i = 0; i < renwuTemplate.getQingdanDTOList().size(); i++) {
                    renwuTemplate.setQingdanDTO(renwuTemplate.getQingdanDTOList().get(i));
                    String daochuUrl = daochumoban(renwuTemplate);
                    urlList.add(daochuUrl);
                }
            } else {
                String daochuUrl =daochumoban(renwuTemplate);
                urlList.add(daochuUrl);
            }
            return urlList;
        }
    
        public static String daochumoban(RenwuTemplateDTO renwuTemplate) throws IOException {
            // 为表格的显示绑定行循环
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            // 将bz设置为行循环绑定的数据源的key,即key是bz的value会在模板中的{{bz}}处进行解析
            Configure configure = Configure.builder().bind("bz", policy).build();
            // 图片标签集合
            List<String> pictureTag = new ArrayList<>();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            HashMap<String, Object> dataMap = new HashMap<String, Object>() {
                {
                    //添加文本
                    put("xiangmuName", renwuTemplate.getXiangmuName());
                    put("xiangmuzhouqi", renwuTemplate.getXiangmuzhouqi());
                    put("renwuName", renwuTemplate.getRenwuName());
                    put("renwuzhouqi", sdf.format(renwuTemplate.getStartTime()) + " 至 " + sdf.format(renwuTemplate.getEndTime()));
                    put("description", renwuTemplate.getDescription());
                    String xiangmuLink = "";
                    if (renwuTemplate.getRenwuResourceUrlList() != null && renwuTemplate.getRenwuResourceUrlList().size() > 0) {
                        for (int i = 0; i < renwuTemplate.getRenwuResourceUrlList().size(); i++) {
                            if (i != renwuTemplate.getRenwuResourceUrlList().size()-1) {
                                xiangmuLink += PeizhiConfig.getUploadPrefix() + renwuTemplate.getRenwuResourceUrlList().get(i) + "\n";
                            } else {
                                xiangmuLink += PeizhiConfig.getUploadPrefix() + renwuTemplate.getRenwuResourceUrlList().get(i);
                            }
                        }
                    }
                    put("xiangmulink", xiangmuLink );
                    put("biaoqianName", renwuTemplate.getQingdanDTO() != null ? renwuTemplate.getQingdanDTO().getBiaoqianName() : "");
                    String diliurk = PeizhiConfig.getUploadUrl() + renwuTemplate.getQingdanDTO().getResourceUrl();
                    PictureRenderData pictureRenderData = Pictures.ofStream(Files.newInputStream(Paths.get(diliurk)), PictureType.PNG)
                            .size(200, 150).create();
                    put("dililink", pictureRenderData);
                    put("resourceDescription", renwuTemplate.getQingdanDTO() != null ? renwuTemplate.getQingdanDTO().getDescription() : "");
                    put("startYear", renwuTemplate.getQingdanDTO() != null ? renwuTemplate.getQingdanDTO().getStartYear() : "");
                    put("endYear", renwuTemplate.getQingdanDTO() != null ? renwuTemplate.getQingdanDTO().getEndYear(): "");
                    put("area", renwuTemplate.getQingdanDTO() != null ? renwuTemplate.getQingdanDTO().getArea() : "");
                    // 其他业务获取到数据源
                    String testTable = null;
                    if (renwuTemplate.getQingdanDTO() != null && renwuTemplate.getQingdanDTO().getQingdanExtList() != null &&  renwuTemplate.getQingdanDTO().getQingdanExtList().size() > 0) {
                        String str = "";
                        for (int i = 0; i < renwuTemplate.getQingdanDTO().getQingdanExtList().size(); i++) {
                            SysQingdanExtEntity ext = renwuTemplate.getQingdanDTO().getQingdanExtList().get(i);
                            String templateType = null, data = PeizhiConfig.getUploadPrefix() + ext.getResourceUrl();
                           // PictureRenderData pictureRenderData1 = null;
                            if (ext.getTemplateType() == 1) {
                                templateType = "图片";
                                //String dataUrl = PeizhiConfig.getUploadUrl() + ext.getResourceUrl();
                                //pictureRenderData1 = Pictures.ofStream(Files.newInputStream(Paths.get(dataUrl)), PictureType.PNG)
                                //        .size(200, 150).create();
                            } else if (ext.getTemplateType() == 2) {
                                templateType = "附件";
                            } else if (ext.getTemplateType() == 3) {
                                templateType = "音视频";
                            } else if (ext.getTemplateType() == 4) {
                                templateType = "文本";
                                data = ext.getExtText();
                            } else if (ext.getTemplateType() == 5) {
                                templateType = "文档";
                            }
                            String source = StringUtils.isNotBlank(ext.getSource()) ? ext.getSource() : "";
                            data = StringUtils.isNotBlank(data) ? data : "";
                            str += "{\n" +
                                    "        \"index\": \"" + (i + 1) + "\",\n" +
                                    "        \"templateName\": \"" + ext.getTemplateName() + "\",\n" +
                                    "        \"templateType\": \"" + templateType + "\",\n" +
                                    "        \"source\": \"" + source + "\",\n" +
                                    "        \"data\": \"" + data + "\",\n" +
                                    "    },\n";
    
                        }
                        testTable = "[" + str + "]";
                    }
                    // 内容在表格里循环
                    // JSON使用,需要导入fastjson依赖
                    List<WordQingdanDetailsDTO> forms = JSON.parseArray(testTable, WordQingdanDetailsDTO.class);
                    if (forms != null && forms.size() > 0) {
                        for (int i = 0; i < forms.size(); i++) {
                            put("index" + i, forms.get(i).getIndex());
                            put("templateName" + i, forms.get(i).getTemplateName());
                            put("templateType" + i, forms.get(i).getTemplateType());
                            put("source" + i, forms.get(i).getSource());
                            put("data" + i, forms.get(i).getData());
                        }
                    }
                    put("bz", forms);
                    pictureTag.add("dililink");
                }
            };
            for (String tag : pictureTag ) {
                //设置图片,不然保存的是一串字符
                configure.customPolicy(tag, new PictureRenderPolicy());
            }
            if (!new File(PeizhiConfig.getUploadOutPath()).exists()) {
                new File(PeizhiConfig.getUploadOutPath()).mkdirs();
            }
            String outPath = PeizhiConfig.getUploadOutPath() + "/"+ UUID.randomUUID() +".docx";
            // 读取模板、数据并渲染
    
            XWPFTemplate template = XWPFTemplate.compile(new FileInputStream(PeizhiConfig.getUploadOutPath() + "/任务数据.docx"), configure).render(dataMap);
            //  文件是否已存在,则删除
            File file = new File(outPath);
            if (file.exists()) {
                file.delete();
            }
            // 生成word保存在指定目录
            //template.writeToFile(outPath);
            template.writeAndClose(Files.newOutputStream(Paths.get(outPath)));
            template.close();
            return outPath;
        }
    
        public static void addToZipFile(String filePath, ZipOutputStream zos) throws IOException {
            File file = new File(filePath);
            FileInputStream fis = new FileInputStream(file);
            ZipEntry zipEntry = new ZipEntry(file.getName());
            zos.putNextEntry(zipEntry);
            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {
                zos.write(bytes, 0, length);
            }
            zos.closeEntry();
            fis.close();
        }
    
    
        public static void createDoc() throws Exception {
            // 为表格的显示绑定行循环
            LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
            // 将bz设置为行循环绑定的数据源的key,即key是bz的value会在模板中的{{bz}}处进行解析
            Configure configure = Configure.builder().bind("bz", policy).build();
            List<String> pictureTag = new ArrayList<>();
    
            // 将需要解析的数据放到dataMap中
            HashMap<String, Object> dataMap = new HashMap<String, Object>() {
                {
                    //添加文本
                    put("xiangmuName", "项目名称");
                    put("xiangmuzhouqi", "2024-03-01 至 2024-04-02");
                    put("renwuName", "任务名称");
                    put("renwuzhouqi", "2024-03-05 至 2024-03-26");
                    put("description", "项目描述");
                    put("xiangmulink", "http://www.baidu.com");
                    put("biaoqianName", "标签名称");
                    PictureRenderData pictureRenderData = Pictures.ofStream(Files.newInputStream(Paths.get("D:\\template\\picture\\其他\\yiyan-NewYear.png")), PictureType.PNG)
                            .size(200, 150).create();
                    put("dililink", pictureRenderData);
                    put("resourceDescription", "资源描述");
                    put("startYear", "1997");
                    put("endYear", "2018");
                    put("area", "100.5");
                    // 其他业务获取到数据源
                    String testTable = null;
                    {
                        testTable = "[\n" +
                                "    {\n" +
                                "        \"index\": \"1\",\n" +
                                "        \"templateName\": \"模板内容1\",\n" +
                                "        \"templateType\": \"模板类型1\",\n" +
                                "        \"source\": \"来源1\",\n" +
                                "        \"data\": \"http://www.baidu.com\"\n" +
                                "    },\n" +
                                "    {\n" +
                                "        \"index\": \"2\",\n" +
                                "        \"templateName\": \"模板内容2\",\n" +
                                "        \"templateType\": \"模板类型2\",\n" +
                                "        \"source\": \"来源2\",\n" +
                                "        \"data\": \"http://www.baidu.com111\"\n" +
                                "    },\n" +
                                "    {\n" +
                                "        \"index\": \"3\",\n" +
                                "        \"templateName\": \"模板内容3\",\n" +
                                "        \"templateType\": \"模板类型3\",\n" +
                                "        \"source\": \"来源3\",\n" +
                                "        \"data\": \"http://www.baidu.com222\"\n" +
                                "    }\n" +
                                "]";
                    }
                    // 内容在表格里循环
                    // JSON使用,需要导入fastjson依赖
                    List<WordQingdanDetailsDTO> forms = JSON.parseArray(testTable, WordQingdanDetailsDTO.class);
                    for (int i = 0; i < forms.size(); i++) {
                        put("index" + i, forms.get(i).getIndex());
                        put("templateName" + i, forms.get(i).getTemplateName());
                        put("templateType" + i, forms.get(i).getTemplateType());
                        put("source" + i, forms.get(i).getSource());
                        put("data" + i, forms.get(i).getData());
                    }
                    put("bz", forms);
                    pictureTag.add("dililink");
                }
            };
            for (String tag : pictureTag ) {
                //设置图片,不然保存的是一串字符
                configure.customPolicy(tag, new PictureRenderPolicy());
            }
            String outPath = "D:\\生成数据.docx";
            // 读取模板、数据并渲染
            XWPFTemplate template = XWPFTemplate.compile(new FileInputStream("D:\\任务数据.docx"), configure).render(dataMap);
            //  文件是否已存在,则删除
            File file = new File(outPath);
            if (file.exists()) {
                file.delete();
            }
            // 生成word保存在指定目录
            //template.writeToFile(outPath);
            template.writeAndClose(Files.newOutputStream(Paths.get(outPath)));
            template.close();
        }
    
        public static void main(String[] args) throws Exception {
            createDoc();
        }
    
    }

    3.5 用到的实体类

  • RenwuTemplateDTO

    java 复制代码
    package io.renren.modules.sys.dto;
    
    import com.fasterxml.jackson.annotation.JsonFormat;
    import io.renren.common.validator.group.AddGroup;
    import io.renren.common.validator.group.UpdateGroup;
    import io.renren.modules.sys.entity.SysRenwuTemplateEntity;
    import io.renren.modules.sys.entity.SysResourceEntity;
    import io.renren.modules.sys.entity.SysXiangmuEntity;
    import lombok.Data;
    import org.springframework.format.annotation.DateTimeFormat;
    
    import javax.validation.constraints.NotBlank;
    import javax.validation.constraints.NotNull;
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @Author: Administrator
     * @Date: 2023/12/8
     * @Description:
     */
    @Data
    public class RenwuTemplateDTO {
    
        private Long renwuId;
    
        private Long xiangmuId;
    
        private String renwuName;
    
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        @JsonFormat(pattern = "yyyy-MM-dd")
        private Date startTime;
    
        @DateTimeFormat(pattern = "yyyy-MM-dd")
        @JsonFormat(pattern = "yyyy-MM-dd")
        private Date endTime;
    
    
        private String description;
    
        private String xiangmuName;
    
        private String xiangmuzhouqi;
    
        private List<SysXiangmuEntity> xiangmuList;
    
        private List<SysResourceEntity> fileList = new ArrayList<>();
    
        private QingdanDTO qingdanDTO;
    
    
        /**
         * 用于导出
         */
        private List<QingdanDTO> qingdanDTOList;
    
        private List<String> renwuResourceUrlList;
    }
  • QingdanDTO

    java 复制代码
    import com.fasterxml.jackson.annotation.JsonFormat;
    import io.renren.common.validator.group.AddGroup;
    import io.renren.common.validator.group.UpdateGroup;
    import io.renren.modules.sys.entity.SysQingdanExtEntity;
    import lombok.Data;
    import org.springframework.format.annotation.DateTimeFormat;
    
    import javax.validation.constraints.NotNull;
    import java.util.Date;
    import java.util.List;
    
    /**
     * @Author: Administrator
     * @Date: 2023/12/11
     * @Description:
     */
    @Data
    public class QingdanDTO {
        private Long qingdanId;
    
        private Long renwuId;
    
        private Long userId;
    
        private String biaoqianName;
    
        @DateTimeFormat(pattern = "yyyy")
        @JsonFormat(timezone = "GMT+8", pattern = "yyyy")
        private String startYear;
    
        @DateTimeFormat(pattern = "yyyy")
        @JsonFormat(timezone = "GMT+8", pattern = "yyyy")
        private String endYear;
    
        private String area;
    
        private String resourceUrl;
    
        /**
         * 资源描述
         */
        private String description;
    
        private String xiangmuName;
    
        private String renwuName;
    
        /**
         * 清单详情
         */
        private List<SysQingdanExtEntity> qingdanExtList;
    
        /**
         * 任务周期
         */
        private String renwuzhouqi;
    
        /**
         * 项目周期
         */
        private String xiangmuzhouqi;
    
    }
  • SysQingdanExtEntity

    java 复制代码
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableLogic;
    import lombok.Data;
    
    import java.io.Serializable;
    
    /**
     * @Author: Administrator
     * @Date: 2023/12/11
     * @Description:
     */
    @Data
    public class SysQingdanExtEntity implements Serializable {
        private static final long serialVersionUID = 1L;
    
        private Long qingdanExtId;
    
        private Long qingdanId;
    
        private String templateName;
    
        private Long resourceId;
    
        private String source;
    
        private String extText;
    
        private Integer templateType;
    
        private String resourceUrl;
    }
  • WordQingdanDetailsDTO

    java 复制代码
    import lombok.Data;
    
    /**
     * @Author: Administrator
     * @Date: 2024/3/1
     * @Description:
     */
    @Data
    public class WordQingdanDetailsDTO {
        /**
         * 下标序号
         */
        private Integer index;
        /**
         * 名称
         */
        private String templateName;
        /**
         * 类型
         */
        private String templateType;
        /**
         * 来源
         */
        private String source;
    
        /**
         * 数据
         */
        private String data;
    }
  1. 工具类ConvertUtils

    java 复制代码
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.BeanUtils;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.List;
    
    /**
     * 转换工具类
     *
     * @author Mark sunlightcs@gmail.com
     */
    public class ConvertUtils {
        private static Logger logger = LoggerFactory.getLogger(ConvertUtils.class);
    
        public static <T> T sourceToTarget(Object source, Class<T> target){
            if(source == null){
                return null;
            }
            T targetObject = null;
            try {
                targetObject = target.newInstance();
                BeanUtils.copyProperties(source, targetObject);
            } catch (Exception e) {
                logger.error("convert error ", e);
            }
    
            return targetObject;
        }
    
        public static <T> List<T> sourceToTarget(Collection<?> sourceList, Class<T> target){
            if(sourceList == null){
                return null;
            }
    
            List targetList = new ArrayList<>(sourceList.size());
            try {
                for(Object source : sourceList){
                    T targetObject = target.newInstance();
                    BeanUtils.copyProperties(source, targetObject);
                    targetList.add(targetObject);
                }
            }catch (Exception e){
                logger.error("convert error ", e);
            }
    
            return targetList;
        }
    }
  2. 导出效果

相关推荐
HaiFan.2 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
大梦百万秋3 小时前
Spring Boot实战:构建一个简单的RESTful API
spring boot·后端·restful
斌斌_____4 小时前
Spring Boot 配置文件的加载顺序
java·spring boot·后端
苹果醋35 小时前
React系列(八)——React进阶知识点拓展
运维·vue.js·spring boot·nginx·课程设计
等一场春雨6 小时前
springboot 3 websocket react 系统提示,选手实时数据更新监控
spring boot·websocket·react.js
荆州克莱7 小时前
Golang的性能监控指标
spring boot·spring·spring cloud·css3·技术
AI人H哥会Java8 小时前
【Spring】控制反转(IoC)与依赖注入(DI)—IoC容器在系统中的位置
java·开发语言·spring boot·后端·spring
赖龙8 小时前
springboot restful mybatis连接mysql返回日期格式不对
spring boot·mybatis·restful
自律的kkk8 小时前
SpringBoot中使用AOP切面编程实现登录拦截
java·spring boot·aop·切面编程·登录拦截
武昌库里写JAVA8 小时前
【MySQL】MySQL 通过127.0.0.1和localhost登录的区别
spring boot·spring·毕业设计·layui·课程设计