添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1、选中需要动态生成数据的部分,按 Ctrl+F9 -> 选择编辑域。

2.1、选择邮件合并,并在域代码部分后面加上相应参数,例如 :${videoNum} 'videoNum'是对应的参数名,后面代码编辑的时候会用到。

2.2、如果是表格类型的动态数据,按照2.1的方式设置参数,但参数以 ${video.name} 方式设置,用以后续动态数据的生成。

2、代码编写

2.1 pom导入
        <!--        word生成-->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>4.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>4.1.1</version>
        </dependency>
        <dependency>
            <groupId>org.jxls</groupId>
            <artifactId>jxls</artifactId>
            <version>2.6.0</version>
            <exclusions>
                <exclusion>
                    <groupId>ch.qos.logback</groupId>
                    <artifactId>logback-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.jxls</groupId>
            <artifactId>jxls-poi</artifactId>
            <version>1.2.0</version>
        </dependency>
        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.xdocreport.core</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.xdocreport.document</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.xdocreport.template</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>fr.opensagres.xdocreport</groupId>
            <artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
            <version>2.0.2</version>
        </dependency>
        <dependency>
            <groupId>org.freemarker</groupId>
            <artifactId>freemarker</artifactId>
            <version>2.3.23</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.5</version>
        </dependency>
2.2、代码编写
2.2.1、编写控制层
import com.fongtech.cli.report.service.ReportService;
import com.fongtech.cli.report.util.ResUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
 * @Author: lin
 * @Date: 2022/1/10 14:25
 * @Version 1.0
@Slf4j
@RestController
@RequestMapping("/report")
@Api(tags = "报表相关接口")
public class ReportController {
    @Autowired
    private ReportService reportService;
    @GetMapping("/exportReportExcel")
    @ApiOperation("导出数据分析报告")
    public Object exportReportExcel(HttpServletRequest request,
                                    @RequestParam(value = "startDate", required = false) String startDate,
                                    @RequestParam(value = "endDate", required = false) String endDate,
                                    @RequestParam(value = "type", required = true) String type) {
        log.info("数据分析报告导出 开始时间:" + startDate + " 结束时间:" + endDate + " 类型:" + type);
        byte[] data = reportService.getWordByte(startDate, endDate, type);
        return ResUtil.getStreamData(request, data, "wordReportExport-" + type, "docx");
2.2.2、编写service层
* @Author: linbo * @Date: 2022/1/10 14:25 * @Version 1.0 public interface ReportService { * 报告导出 * @param startDate * @param endDate * @return byte[] getWordByte(String startDate,String endDate, String type);
import com.fongtech.cli.report.util.ExportData;
import com.fongtech.cli.report.service.ReportService;
import com.fongtech.cli.report.util.SoMap;
import com.fongtech.cli.report.util.WordUtil;
import com.fongtech.cli.report.vo.ReportVo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.util.*;
 * @Author: lin
 * @Date: 2022/1/7 10:13
 * @Version 1.0
@Service
public class ReportServiceImpl implements ReportService {
     * 模板文件的地址
    @Value("${report.model-path-day}")
    private String MODEL_PATH_DAY;
    @Override
    public byte[] getWordByte(String startDate, String endDate, String type) {
        //准备数据
        ExportData evaluation = WordUtil.createExportData(MODEL_PATH_DAY);
        //时间设置
        String[] date = getDate(startDate);
        evaluation.setData("beginYear", date[0]);
        evaluation.setData("beginMonth", date[1]);
        evaluation.setData("beginDay", date[2]);
        String[] dateEnd = getDate(endDate);
        evaluation.setData("endYear", dateEnd[0]);
        evaluation.setData("endMonth", dateEnd[1]);
        evaluation.setData("endDay", dateEnd[2]);
        evaluation.setData("videoNum", "10");
        //设置视频监控状态
        List<SoMap> videoList = new ArrayList<SoMap>();
        for (int i = 0; i < 5; i++) {
            ReportVo reportVo = new ReportVo();
            reportVo.setName("园区名称" + i);
            reportVo.setPark("园区" + i);
            reportVo.setCondition("设备状态" + i);
            videoList.add(new SoMap(reportVo));
        evaluation.setTable("video", videoList);
        // 获取新生成的文件流
        byte[] data = evaluation.getByteArr();
        return data;
    private String[] getDate(String date) {
        if (date.contains(" ")) {
            return date.split("-| ");
        } else {
            return date.split("-");

application.yml文件:

report:
  model-path-day: "E://modelDay.docx"  #根据模板文件位置设定
2.2.3、Utile类设置

ExportData:

import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.images.ByteArrayImageProvider;
import fr.opensagres.xdocreport.document.images.IImageProvider;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
import org.springframework.core.io.ClassPathResource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
public class ExportData {
    private IXDocReport report;
    private IContext context;
     * 构造方法
     * @param report
     * @param context
    public ExportData(IXDocReport report, IContext context) {
        this.report = report;
        this.context = context;
     * 设置普通数据,包括基础数据类型,数组,试题对象
     * 使用时,直接 ${key.k} 或者 [#list d as key]
     * @param key   健
     * @param value 值
    public void setData(String key, Object value) {
        context.put(key, value);
     * 设置表格数据,用来循环生成表格的 List 数据
     * 使用时,直接 ${key.k}
     * @param key   健
     * @param maps List 集合
    public void setTable(String key, List<SoMap> maps) {
        FieldsMetadata metadata = report.getFieldsMetadata();
        metadata = metadata == null ? new FieldsMetadata() : metadata;
        SoMap map = maps.get(0);
        for (String kk : map.keySet()) {
            metadata.addFieldAsList(key + "." + kk);
        report.setFieldsMetadata(metadata);
        context.put(key, maps);
     * 设置图片数据
     * 使用时 直接在书签出 key
     * @param key 健
     * @param url 图片地址
    public void setImg(String key, String url) {
        FieldsMetadata metadata = report.getFieldsMetadata();
        metadata = metadata == null ? new FieldsMetadata() : metadata;
        metadata.addFieldAsImage(key);
        report.setFieldsMetadata(metadata);
        try (
                InputStream in = new ClassPathResource(url).getInputStream();
            IImageProvider img = new ByteArrayImageProvider(in);
            context.put(key, img);
        } catch (IOException ex) {
            throw new RuntimeException(ex.getMessage());
     * 获取文件流数据
     * @return 文件流数组
    public byte[] getByteArr() {
        try (
                ByteArrayOutputStream out = new ByteArrayOutputStream();
            report.process(context, out);
            return out.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex.getMessage());
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
public class ResUtil {
     * 生成下载文件,浏览器直接访问为下载文件
     * @param request  请求对象
     * @param data     数据流数组
     * @param prefix   下载的文件名
     * @param suffix   文件后缀
     * @return 浏览器可以直接下载的文件流
    public static ResponseEntity<byte[]> getStreamData(
            HttpServletRequest request, byte[] data, String prefix, String suffix
        HttpHeaders headers = new HttpHeaders();
        prefix = StringUtils.isEmpty(prefix) ? "未命名" : prefix;
        suffix = suffix == null ? "" : suffix;
        try {
            String agent = request.getHeader("USER-AGENT");
            boolean isIE = null != agent, isMC = null != agent;
            isIE = isIE && (agent.indexOf("MSIE") != -1 || agent.indexOf("Trident") != -1);
            isMC = isMC && (agent.indexOf("Mozilla") != -1);
            prefix = isMC ? new String(prefix.getBytes("UTF-8"), "iso-8859-1") :
                    (isIE ? java.net.URLEncoder.encode(prefix, "UTF8") : prefix);
            headers.setContentDispositionFormData("attachment", prefix + "." + suffix);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            return new ResponseEntity<byte[]>(data, headers, HttpStatus.OK);
        } catch (UnsupportedEncodingException ex) {
            ex.printStackTrace();
            throw new RuntimeException(ex.getMessage());
package com.fongtech.cli.report.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
 * @Author: linbo
 * @Date: 2022/1/4 11:38
 * @Version 1.0
public class SoMap extends HashMap<String, Object> {
    public SoMap() { }
     * 构造方法,将任意实体类转化为 Map
     * @param obj
    public SoMap(Object obj) {
        Class clazz = obj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        try {
            for (Field field : fields) {
                field.setAccessible(true);
                this.put(field.getName(), field.get(obj));
        } catch (IllegalAccessException ex) {
            throw new RuntimeException(ex.getMessage());
     * 将 Map 转化为 任意实体类
     * @param clazz 反射获取类字节码对象
     * @return
    public <T> T toEntity(Class<T> clazz) {
        Field[] fields = clazz.getDeclaredFields();
        try {
            Constructor constructor = clazz.getDeclaredConstructor();
            T t = (T) constructor.newInstance();
            for (Field field : fields) {
                field.setAccessible(true);
                field.set(t, this.get(field));
            return t;
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage());
     * 从集合中获取一个字段的方法,如果字段不存在返回空
     * @param key  字段的唯一标识
     * @param <T>  字段的类型,运行时自动识别,使用时无需声明和强转
     * @return     对应字段的值
    public <T> T get(String key) {
        return (T) super.get(key);
package com.fongtech.cli.report.util;
import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import java.io.FileInputStream;
import java.io.InputStream;
public class WordUtil {
     * 获取 Word 模板的两个操作对象 IXDocReport 和 IContext
     * @param path 模板绝对地址
     * @return 模板数据对象
    public static ExportData createExportData(String path) {
        try {
            IXDocReport report = createReport(path);
            IContext context = report.createContext();
            return new ExportData(report, context);
        } catch (XDocReportException ex) {
            throw new RuntimeException(ex.getMessage());
     * 加载模板的方法,主要是指定模板的路径和选择渲染数据的模板
     * @param url 模板相对于类路径的地址
     * @return word 文档操作类
    private static IXDocReport createReport(String url) {
        try (
                InputStream in = new  FileInputStream(url);
            IXDocReport ix = XDocReportRegistry.getRegistry().loadReport(in, TemplateEngineKind.Freemarker);
            return ix;
        } catch (Exception ex) {
            throw new RuntimeException(ex.getMessage());
package com.fongtech.cli.report.vo;
import com.fongtech.cli.framework.model.BaseModel;
import java.io.Serializable;
 * @Author: linbo
 * @Date: 2022/1/4 13:47
 * @Version 1.0
public class ReportVo extends BaseModel implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private String transmissibility;
    private String condition;
    private String park;
    public String getPark() {
        return park;
    public void setPark(String park) {
        this.park = park;
    public String getName() {
        return name;
    public void setName(String name) {
        this.name = name;
    public String getTransmissibility() {
        return transmissibility;
    public void setTransmissibility(String transmissibility) {
        this.transmissibility = transmissibility;
    public String getCondition() {
        return condition;
    public void setCondition(String condition) {
        this.condition = condition;

3、数据导出

在这里插入图片描述

分类:
后端
  • 就叫这个名儿吧
  •