poi-tl实现导出word功能+phantomjs后台生成echarts截图

本文主要说明一下如何通过poi-tl实现word导出,并在导出文档中插入表格、echarts的图片。

1)下载phantomjs安装包,下载地址为https://phantomjs.org/download.html ,根据操作系统下载对应的安装包即可;windows操作系统安装配置参考资料https://blog.csdn.net/qq_42543250/article/details/81542878,linux系统安装配置参考资料https://blog.csdn.net/wanght89/article/details/78320375

2)若应用部署在linux系统上,导出的word文档可能会出现字体不显示的问题,可以将windows下的字体放到linux系统中,具体操作可参考资料:https://www.jb51.net/article/136288.htm

3)将echarts.min.js ,echarts-convert.js ,jquery-2.1.4.min.js三个js文件放到同一目录下,主要是用于后台生成echarts图标然后通过phantomjs截图到本地保存。
echarts-convert.js内容如下:

(function () {
var system = require('system');
var fs = require('fs');
var config = {
    // jquery-2.1.4.min.js文件名称一定要对应上
    JQUERY: 'jquery-2.1.4.min.js',
    // echarts.min.js文件名称一定要对应上
    ECHARTS: 'echarts.min.js',
    // default container width and height
    DEFAULT_WIDTH: '600',
    DEFAULT_HEIGHT: '700'
}, parseParams, render, pick, usage;

usage = function () {
    console.log("\nUsage: phantomjs echarts-convert.js -options options -outfile filename -width width -height height"
        + "OR"
        + "Usage: phantomjs echarts-convert.js -infile URL -outfile filename -width width -height height\n");
};

pick = function () {
    var args = arguments, i, arg, length = args.length;
    for (i = 0; i < length; i += 1) {
        arg = args[i];
        if (arg !== undefined && arg !== null && arg !== 'null' && arg != '0') {
            return arg;
        }
    }
};

parseParams = function () {
    var map = {}, i, key;
    if (system.args.length < 2) {
        usage();
        phantom.exit();
    }
    for (i = 0; i < system.args.length; i += 1) {
        if (system.args[i].charAt(0) === '-') {
            key = system.args[i].substr(1, i.length);
            if (key === 'infile') {
                // get string from file
                // force translate the key from infile to options.
                key = 'options';
                try {
                    map[key] = fs.read(system.args[i + 1]).replace(/^\s+/, '');
                } catch (e) {
                    console.log('Error: cannot find file, ' + system.args[i + 1]);
                    phantom.exit();
                }
            } else {
                map[key] = system.args[i + 1].replace(/^\s+/, '');
            }
        }
    }
    return map;
};

render = function (params) {
    var page = require('webpage').create(), createChart;

    var bodyMale = config.SVG_MALE;
    page.onConsoleMessage = function (msg) {
        console.log(msg);
    };

    page.onAlert = function (msg) {
        console.log(msg);
    };

    createChart = function (inputOption, width, height,config) {
        var counter = 0;
        function decrementImgCounter() {
            counter -= 1;
            if (counter < 1) {
                console.log(messages.imagesLoaded);
            }
        }

        function loadScript(varStr, codeStr) {
            var script = $('<script>').attr('type', 'text/javascript');
            script.html('var ' + varStr + ' = ' + codeStr);
            document.getElementsByTagName("head")[0].appendChild(script[0]);
            if (window[varStr] !== undefined) {
                console.log('Echarts.' + varStr + ' has been parsed');
            }
        }

        function loadImages() {
            var images = $('image'), i, img;
            if (images.length > 0) {
                counter = images.length;
                for (i = 0; i < images.length; i += 1) {
                    img = new Image();
                    img.onload = img.onerror = decrementImgCounter;
                    img.src = images[i].getAttribute('href');
                }
            } else {
                console.log('The images have been loaded');
            }
        }
        // load opitons
        if (inputOption != 'undefined') {
            // parse the options
            loadScript('options', inputOption);
            // disable the animation
            options.animation = false;
        }

        // we render the image, so we need set background to white.
        $(document.body).css('backgroundColor', 'white');
        var container = $("<div>").appendTo(document.body);
        container.attr('id', 'container');
        container.css({
            width: width,
            height: height
        });
        // render the chart
        var myChart = echarts.init(container[0]);
        myChart.setOption(options);
        // load images
        loadImages();
        return myChart.getDataURL();
    };

    // parse the params
    page.open("about:blank", function (status) {
        // inject the dependency js
        page.injectJs(config.ESL);
        page.injectJs(config.JQUERY);
        page.injectJs(config.ECHARTS);


        var width = pick(params.width, config.DEFAULT_WIDTH);
        var height = pick(params.height, config.DEFAULT_HEIGHT);

        // create the chart
        var base64 = page.evaluate(createChart, params.options, width, height,config);
        fs.write("base64.txt",base64);
        // define the clip-rectangle
        page.clipRect = {
            top: 0,
            left: 0,
            width: width,

            height: height
        };
        // render the image
        page.render(params.outfile);
        console.log('render complete:' + params.outfile);
        // exit
        phantom.exit();
    });
};
// get the args
var params = parseParams();

// validate the params
if (params.options === undefined || params.options.length === 0) {
    console.log("ERROR: No options or infile found.");
    usage();
    phantom.exit();
}
// set the default out file
if (params.outfile === undefined) {
    var tmpDir = fs.workingDirectory + '/tmp';
    // exists tmpDir and is it writable?
    if (!fs.exists(tmpDir)) {
        try {
            fs.makeDirectory(tmpDir);
        } catch (e) {
            console.log('ERROR: Cannot make tmp directory');
        }
    }
    params.outfile = tmpDir + "/" + new Date().getTime() + ".png";
}

// render the image
render(params);
}());

4)添加所需依赖包,如下:

 <!--poi begin要指定poi的版本,否则xdocreport会不匹配-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-scratchpad</artifactId>
            <version>3.17</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-schemas</artifactId>
            <version>1.4</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>ooxml-security</artifactId>
            <version>1.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.apache.xmlbeans</groupId>
                    <artifactId>xmlbeans</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--xdocreport-->

        <!-- poi-tl-start -->
        <dependency>
            <groupId>com.deepoove</groupId>
            <artifactId>poi-tl</artifactId>
            <version>1.6.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-chrome-driver</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-firefox-driver</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-ie-driver</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-safari-driver</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-support</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-remote-driver</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-api</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>org.seleniumhq.selenium</groupId>
            <artifactId>selenium-java</artifactId>
            <version>2.45.0</version>
        </dependency>
        <dependency>
            <groupId>com.codeborne</groupId>
            <artifactId>phantomjsdriver</artifactId>
            <version>1.2.1</version>
            <exclusions>
                <exclusion>
                    <groupId>org.seleniumhq.selenium</groupId>
                    <artifactId>selenium-java</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.seleniumhq.selenium</groupId>
                    <artifactId>selenium-remote-driver</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>ch.ethz.ganymed</groupId>
            <artifactId>ganymed-ssh2</artifactId>
            <version>262</version>
        </dependency>

5)项目application.yml文件中添加配置:

phantomjs:
  host: 182.168.1.1   // 安装phantomjs的服务器地址
  port: 22            // 安装phantomjs的服务器端口号
  username: root      // 安装phantomjs的服务器用户名
  password: root      // 安装phantomjs的服务器密码
  jsPath: /root/echarts/echarts-convert.js   // 安装phantomjs的服务器js文件路径
  echartsImg: /root/echartsImg/              // 安装phantomjs的服务器生成echarts截图存放路径
  dockerDocxPath: /app/poiDocx/              // 应用服务器生成word文档存放路径

6)本人部署应用用的是docker容器,所以需要将容器内路径和宿主机路径做一个映射,因为我的phantomjs是部署在宿主机的。
在这里插入图片描述

7)word模板如下,
在这里插入图片描述

在这里插入图片描述

8)controller层 :

    @ApiOperation("生成安全生产违章分析报告")
    @GetMapping("/generateSafeReportFile")
    public Response generateSafeReportFile(HttpServletResponse response,String startDate,String endDate) {

        try {
            Date currentDate = new Date();
            long random = currentDate.getTime();
            String docFilePath = PoiCommonUtil.getDocFilePath("safeReport"+random);

            // 生成word文档数据
            Map<String,Object> objMap = supervisorService.getSafeReportMap(startDate,endDate);

            //渲染word文档中的表格,需要绑定
            Configure config = Configure.newBuilder().bind("sceneList", new DetailTablePolicy()).
                                                      bind("weizhangTable",new DetailTablePolicy()).
                                                      bind("weizhangDetailTable",new DetailTablePolicy()).
                                                      bind("sceneAndWeizhangTable",new SceneAndWeizhangTablePolicy()).
                                                      bind("weizhangAnalyseTable",new NormalTablePolicy()).build();
            poiCommonUtil.exportFile(PoiConstant.SAFE_REPORT_TEMPLATE, objMap, docFilePath, config);


            //导出word
            if (StringUtils.isNotEmpty(docFilePath)) {
                OperationUtil.exportFileStreamByFilePath(response,docFilePath,PoiConstant.SAFE_REPORT);
            }


            return Response.success("导出成功!");
        } catch (Throwable e) {
            log.error("POI生成错误:", e);
            return Response.failure("导出失败!");
        }

    }

9)PoiCommonUtil工具类:

package com.zhxd.cloud.ksh.utils.poi;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.data.PictureRenderData;
import com.google.common.collect.Maps;
import com.zhxd.cloud.ksh.supervisor.constant.PoiConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.stereotype.Service;

import java.io.*;
import java.util.Map;

/**
 * @Auther: mm
 * @Date: 2019/12/17 09:04
 * @Description:
 */
@Service
@Slf4j
public class PoiCommonUtil {

    /**
     * 获取导出文件全路径
     * 如果对应文件存在则删除
     *
     * @return
     */
    public static String getDocFilePath(String docFileNamePrefix) {
        try {
            ApplicationHome h = new ApplicationHome(PoiCommonUtil.class);
            File jarF = h.getSource();
            String docOutDir = jarF.getParentFile().toString() + File.separator + "poiDocx" + File.separator;

            File docFile = new File(docOutDir);
            if (!docFile.exists()) {
                docFile.mkdirs();
            }

            String filePath = docOutDir + docFileNamePrefix + ".docx";
            File dayReportFile = new File(filePath);
            if (dayReportFile.exists()) {
                dayReportFile.delete();
            }

            log.info("filePath={}", filePath);
            return filePath;
        } catch (Exception e) {
            log.error("获取文件目录报错:", e);
            return "";
        }
    }

    /**
     * @param templateFileName
     * @param obj
     * @return
     */
    public String exportFile(String templateFileName, Object obj, String outPath, Configure config ) {
        try {
            XWPFTemplate template = null;
            if(config!=null){
                template = XWPFTemplate.compile(this.getClass().getResourceAsStream(PoiConstant.DOCX_TEMPLATE_PATH +templateFileName),config);
            }else{
                template = XWPFTemplate.compile(this.getClass().getResourceAsStream(PoiConstant.DOCX_TEMPLATE_PATH + templateFileName));
            }
            template.render(obj);
            FileOutputStream out = new FileOutputStream(outPath);
            template.write(out);
            out.flush();
            out.close();
            template.close();
            return outPath;
        } catch (IOException e) {
            log.error("poi生成文件报错:{}", e);
            return "";
        }
    }

    public static PictureRenderData generateEChart(String JSpath,String echartsImg,String hostname,int port,String username,String password,String options, String imgName,int width,int height) {

        ApplicationHome h = new ApplicationHome(PoiCommonUtil.class);
        File jarF = h.getSource();
        String dockerEchartsImg = jarF.getParentFile().toString() + File.separator + "echartsImg" + File.separator;

        writeFile(dockerEchartsImg,options,imgName.substring(0,imgName.indexOf(".")));
        String echartsImgPath =  echartsImg + imgName;
        String echartsJsonPath =  echartsImg + imgName.substring(0,imgName.indexOf(".")) + ".json";
        try {

            String cmd = "phantomjs " + JSpath + "  -infile " + echartsJsonPath + " -outfile " + echartsImgPath + " -width " + width + " -height " + height;
            execCommand(hostname,port,username,password,cmd);

            PictureRenderData brd = new PictureRenderData(width, height, new File(dockerEchartsImg+imgName));

            return brd;
        } catch (Exception e) {

            log.error("生成echarts截图报错了,{}",e);

            return null;
        }
    }

    public static void execCommand(String hostname,int port,String username,String password,String execStr) {
        log.info(execStr + "------------------------");
        try{
            Connection conn = new Connection(hostname,port);
            conn.connect();
            boolean isAuthenticated = conn.authenticateWithPassword(username, password);

            if (isAuthenticated == false) {
                throw new IOException("Authentication failed.");
            }

            Session sess = conn.openSession();
            sess.execCommand(execStr);

            InputStream stdout = new StreamGobbler(sess.getStdout());
            BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
            while (true){
                String line = br.readLine();
                if (line == null)
                    break;
            }

            sess.close();
            conn.close();

        } catch (Exception e) {
            System.exit(2);
        }
    }

    public static void createNewFile(File file) throws IOException {
        if (!file.exists()) {   //文件不存在则创建文件,先创建目录
            File dir = new File(file.getParent());
            dir.mkdirs();
            file.createNewFile();
        }
    }

    /*
     *
     * options生成文件存储
     */
    public static void writeFile(String echartsConvertPath,String options, String imgName) {
        String dataPath = echartsConvertPath + imgName + ".json";
        try {
            /* option写入文本文件 用于执行命令*/
            File writename = new File(dataPath);
            createNewFile(writename);
            BufferedWriter out = new BufferedWriter(new FileWriter(writename));
            out.write(options);
            out.flush(); // 把缓存区内容压入文件
            out.close(); // 最后关闭文件
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Map<String,Object> getLabelMapOfEcharts() {
        Map<String,Object> labelMap = Maps.newHashMap();

        labelMap.put("show",true);
        labelMap.put("position","top");

        return labelMap;
    }

    public static Map<String,Object> getLineStyleMapOfLine(String color,int width) {
        Map<String,Object> lineStyleMap = Maps.newHashMap();
        lineStyleMap.put("color",color);
        lineStyleMap.put("width",width);
        lineStyleMap.put("type","dashed");

        return lineStyleMap;
    }
}

10)OperationUtil工具类:

package com.zhxd.cloud.ksh.utils;

import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

@Slf4j
public class OperationUtil {


    /**
     * 根据文件路径下载/导出文件
     * @param response
     * @param docFilePath
     * @param moduleName
     */
    public static void exportFileStreamByFilePath(HttpServletResponse response,String docFilePath, String moduleName){
        try {
            String fileName ;
            if (!Strings.isNullOrEmpty(docFilePath)) {
                fileName = docFilePath.substring(docFilePath.lastIndexOf(File.separator)+1);
                response.addHeader("Content-Disposition", "attachment;filename=\""
                        + new String(fileName.getBytes("UTF-8"),"ISO-8859-1") + "\"");
                response.setContentType("application/msword; charset=UTF-8");
                byte[] buffer = new byte[4096];
                BufferedOutputStream output = null;
                BufferedInputStream input = null;
                try {
                    File file = new File(docFilePath);
                    if(!file.exists()){
                        log.error("不存在对应路径的文件!");
                        return;
                    }
                    output = new BufferedOutputStream(response.getOutputStream());
                    input = new BufferedInputStream(new FileInputStream(docFilePath));
                    int n = -1;
                    while ((n = input.read(buffer, 0, 4096)) != -1) {
                        output.write(buffer, 0, n);
                    }
                    response.flushBuffer();
                } catch (Exception e) {
                    log.error("根据路径导出{}文件报错:{}",moduleName,e);
                } finally {
                    try {
                        if (input != null)
                            input.close();
                        if (output != null)
                            output.close();
                    } catch (IOException e) {
                        log.error("根据路径导出{}文件报错:{}",moduleName,e);
                    }
                }
            }
        } catch (Exception e) {
            log.error("文件导出错误:" ,e);
        }
    }

}

11)serviceImpl实现类代码:

 @Override
    public Map<String, Object> getSafeReportMap(String startDate, String endDate) throws Exception {
        // 表格样式
        TableStyle rowStyle = new TableStyle();
        rowStyle.setAlign(STJc.CENTER);

        // 返回前端的数据
        Map<String,Object> objMap = new HashMap<>();
        objMap.put("startDate",startDate);
        objMap.put("endDate",endDate);

        // 1, 表格数据开始 --- 根据部门分组统计现场督查情况
        List<RtRiskSupervisor> sceneList = riskSupervisorMapper.listSupervisorGroupByDept(startDate,endDate);

        // 构建表内容
        List<RowRenderData> rowDataList = Lists.newArrayList();

        if (!CollectionUtils.isEmpty(sceneList)) {
            for (RtRiskSupervisor riskSupervisor:sceneList) {

                // 处理表格的数据
                RowRenderData rowData1 = RowRenderData.build("测试","数量",
                        "1","1",
                        "1","1",
                        "1","1",
                        "1","1",
                        "1");

                RowRenderData rowData2 = RowRenderData.build("测试","占比",
                        "1","1",
                        "1","1",
                        "1","1",
                        "1","1",
                        "1");

                rowData1.setRowStyle(rowStyle);
                rowData2.setRowStyle(rowStyle);

                rowDataList.add(rowData1);
                rowDataList.add(rowData2);
        }
        objMap.put("sceneList",rowDataList);
        // 1, 表格数据结束 --- 根据部门分组统计现场督查情况


        // 2,echarts截图开始 --- 统计违章数据-柱形图
        List<Map<String,Object>> weizhangList = riskSupervisorMapper.listWeizhangNumGroupByDept(startDate,endDate);

        PictureRenderData pictureRenderData = null;// 违章柱形图
        if (!CollectionUtils.isEmpty(weizhangList)) {
            List<String> xDataList = new ArrayList<>();// 违章柱图的x轴数据
            List<Object> yDataList = new ArrayList();// 违章柱图的y轴数据
            for (Map<String,Object> weizhangMap:weizhangList) {
                xDataList.add((String) weizhangMap.get("orgInspectDept"));
                yDataList.add(weizhangMap.get("problemNum"));
            }

            pictureRenderData = createEchartsBarImg(xDataList,yDataList,PoiConstant.WEIZHANG_BAR_IMG,400,300);
        }

        objMap.put("weizhangBarImg",pictureRenderData);
        // 2,echarts截图结束 --- 统计违章数据-柱形图

        return objMap;
    }

  // 柱图
    private PictureRenderData createEchartsBarImg(List<String> xDataList,List<Object> yDataList,String imgName,int width,int height) {
        JSONObject weizhangJson = new JSONObject();// 违章柱图的json数据

        Map<String,Object> xAxisMap = new HashMap<>();
        xAxisMap.put("type","category");
        xAxisMap.put("data",xDataList);

        Map<String,Object> yAxisMap = new HashMap<>();
        yAxisMap.put("type","value");

        List<Map<String,Object>> seriesList = Lists.newArrayList();
        Map<String,Object> seriesMap = new HashMap<>();
        Map<String,Object> labelMap = new HashMap<>();
        labelMap.put("show",true);
        labelMap.put("position","top");

        seriesMap.put("data",yDataList);
        seriesMap.put("type","bar");
        seriesMap.put("label",labelMap);
        seriesList.add(seriesMap);

        weizhangJson.put("xAxis",xAxisMap);
        weizhangJson.put("yAxis",yAxisMap);
        weizhangJson.put("series",seriesList);
        weizhangJson.put("color","#5470c6");

        String weizhangJsonStr = JSONObject.toJSONString(weizhangJson);
        PictureRenderData pictureRenderData = PoiCommonUtil.generateEChart(phantomjsConfig.getJsPath(),phantomjsConfig.getEchartsImg(),phantomjsConfig.getHost(),phantomjsConfig.getPort(),phantomjsConfig.getUsername(),phantomjsConfig.getPassword(),weizhangJsonStr, imgName,width,height);

        return pictureRenderData;
    }

12)phantomjsConfig代码如下:

package com.zhxd.cloud.ksh.supervisor.constant;

import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
public class PhantomjsConfig {

    @Value("${phantomjs.host}")
    private String host;

    @Value("${phantomjs.port}")
    private int port;

    @Value("${phantomjs.username}")
    private String username;

    @Value("${phantomjs.password}")
    private String password;

    @Value("${phantomjs.jsPath}")
    private String jsPath;

    @Value("${phantomjs.echartsImg}")
    private String echartsImg;

    @Value("${phantomjs.dockerDocxPath}")
    private String dockerDocxPath;
}

13)DetailTablePolicy文件需要继承DynamicTableRenderPolicy重写方法,这样可以自己定义单元格合并的规则,代码如下:

package com.zhxd.cloud.ksh.utils.poi;

import com.deepoove.poi.data.RowRenderData;
import com.deepoove.poi.policy.DynamicTableRenderPolicy;
import com.deepoove.poi.policy.MiniTableRenderPolicy;
import com.deepoove.poi.util.TableTools;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.springframework.util.CollectionUtils;

import java.util.List;

public class DetailTablePolicy extends DynamicTableRenderPolicy {

    // 填充数据所在行数
    int listsStartRow = 1;

    @Override
    public void render(XWPFTable table, Object data) {
        if (null == data) return;
        List<RowRenderData> dataList = (List<RowRenderData>) data;

        if (!CollectionUtils.isEmpty(dataList)) {
            table.removeRow(listsStartRow);
            // 循环插入行
            for (int i = dataList.size()-1; i >=0; i--) {
                RowRenderData row = dataList.get(i);

                XWPFTableRow insertNewTableRow = table.insertNewTableRow(listsStartRow);
                for (int j = 0; j < row.getCellDatas().size(); j++){
                    insertNewTableRow.createCell();
                }

                MiniTableRenderPolicy.Helper.renderRow(table, listsStartRow, row);
            }
            //处理合并,定义单元格合并规则
            for (int i=0;i<dataList.size();i++) {

                if (i%2 == 0) {
                    TableTools.mergeCellsVertically(table, 0, i+1, i+2);
                    XWPFTableCell cell = table.getRow(i+1).getCell(0);
                    cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER); //垂直居中
                }
            }
        }
    }
}

14)生成word效果如图
在这里插入图片描述

在这里插入图片描述

15)说明,我们的服务是部署在docker容器中的所以生成截图以及获取文件需要做路径映射,还有其他的兼容配置。下边是我生成表格和echarts截图所参考的博客,供大家对照学习,

https://blog.csdn.net/leftwukaixing/article/details/89449071 ,生成echarts截图 ;
https://blog.csdn.net/qq_26383975/article/details/112238802 ,生成动态表格

POI-tl是一个基于Apache POIJava库,用于动态生成Word文档。它提供了一种简单而强大的方式来根据Word模板生成具有动态内容的文档。在使用POI-tl进行动态导出Word文档时,你需要在模板中定义需要替换的标记,并在代码中使用POI-tl的API来填充这些标记。 首先,你需要在项目中添加POI-tl的依赖。具体的依赖配置可以参考\[1\]中提供的文章。 然后,你需要准备一个Word模板,其中包含需要动态填充的内容。在模板中,你可以使用自定义的标记来标识需要替换的部分。这些标记可以是任意的字符串,但需要与代码中的标记保持一致。 接下来,在代码中,你可以使用POI-tl的API来加载模板并替换其中的标记。你可以使用POI-tl提供的方法来设置文本、图片、表格、页眉、页脚等内容。具体的使用方法可以参考\[3\]中提供的教程。 最后,你可以将生成Word文档导出到文件或直接在浏览器中下载。你可以使用POI-tl提供的方法来实现导出功能。具体的导出方法可以参考\[2\]中提供的代码示例。 总结起来,使用POI-tl动态导出Word文档的步骤包括添加依赖、准备模板、替换标记、导出文档。希望这些信息对你有帮助。 #### 引用[.reference_title] - *1* *2* [SpringBoot+Poi-tl根据Word模板动态生成word(含动态行表格、合并单元格)](https://blog.csdn.net/qq_26383975/article/details/112238802)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [poi-tl导出word](https://blog.csdn.net/weixin_43580824/article/details/129549483)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值