word模板导出word文件

前期准备工作word模板

右键字段如果无编辑域 ctrl+F9 一下,然后再右键

wps 直接 ctrl+F9 会变成编辑域

pom.xml所需依赖

xml 复制代码
<dependencies>
<!--word 依赖-->
<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>
        </dependencies>
        
        <plugins>
        <!--模板是放入项目中,编辑的时候会破坏模板结构,导致模板文件类型不支持
        pom文件增加文件过滤(maven ckean后重试)
-->
        <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>2.6</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <nonFilteredFileExtensions>
                        <nonFilteredFileExtension>docx</nonFilteredFileExtension>
                    </nonFilteredFileExtensions>
                </configuration>
            </plugin>
		</plugins>

Controller 层

java 复制代码
@Slf4j
@RestController
@RequestMapping("/word")
public class WordController{
	@Autowired
    private WordService  word;
	 /**
     * 
     * @param response 
     * @param query 查询参数
     */
    @GetMapping("/export")
    public void export(HttpServletResponse response,  CommonQuery query)
    {
        word.export(response,query);
    }
}

Service 层

java 复制代码
public interface WordService  {
		void export(response,query);
}
java 复制代码
package com.Lichao.word;


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 fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

@Slf4j
@Service
public class WordServiceImpl  {

	@Override
    public void export(HttpServletResponse response, CommonQuery query) {
        try {
            /*
            测试数据
            File file = new File("C:\\Users\\Licha\\Desktop\\模板.docx");
            InputStream ins = new FileInputStream(file);
            */
            //获取Word模板,模板存放路径在项目的resources目录下
            InputStream ins = this.getClass().getResourceAsStream("/template/模板.docx");

            IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
                    TemplateEngineKind.Freemarker);
            IContext context = report.createContext();
            Vo vo = 获取写入数据方法(query);
            //创建要替换的文本变量
            // 字段 ${title}
            context.put("title" , vo.getTitle());
            //集合 -- ${list.type} 
            context.put("list" , vo.listVos());
            //创建字段元数据
            FieldsMetadata fm = report.createFieldsMetadata();
            //Word模板中的表格数据对应的集合类型
            fm.load("list" , ListVo.class, true);
            /*
            //输出到本地目录
            FileOutputStream out = new FileOutputStream(new File("C:\\Users\\Licha\\Desktop\\数据.docx"));
            report.process(context, out);
             */
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/msword");
            String fileName = ledgerVo.getTitle() + ".docx";
            response.setHeader("Content-Disposition" , "attachment;filename="
                    .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
            report.process(context, response.getOutputStream());
        } catch (IOException e) {
            log.error("IOException报错" , e);
        } catch (XDocReportException e) {
            log.error("XDocReportException报错" , e);
        } catch (Exception e) {
            log.error("Exception" , e);
        }
    }
}

参考资料

https://blog.csdn.net/plxddyxnmd/article/details/109129838 - 学习

https://www.cnblogs.com/huigee/p/16588297.html - 解决文件读取乱码bug

模板工具类

java 复制代码
package com.util.xdoc;

import cn.hutool.core.util.ObjectUtil;
import com.leader.task.domain.vo.LedgerInfoDataVo;
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 fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;

/**
 * word模板工具类
 * @author Lsc
 * @date 2023-12-01
 */
public class WordUtil {

    private static final Logger log = LoggerFactory.getLogger(WordUtil.class);


    /**
     *
     * @param response 返回数据
     * @param data 数据   数据字段要与模板中的字段一一对应
     * @param ins 输入流=模板
     * @param title 题目
     */
    public static void exportWord(HttpServletResponse response, Object data , InputStream ins ,String title )
    {
        try {

            IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
                    TemplateEngineKind.Freemarker);

            IContext context = report.createContext();
            Class aClass = data.getClass();
            Field[] declaredFields = aClass.getDeclaredFields();
            Map<String, Class<?>> map = new HashMap<>();
            //创建要替换的文本变量
            for (Field declaredField : declaredFields) {
                declaredField.setAccessible(true);
                context.put(declaredField.getName() , ObjectUtil.isNotEmpty(declaredField.get(data))?declaredField.get(data):"");
                if (declaredField.toString().contains("java.util.List")){
                    String name = declaredField.getName();
                    map.put(name ,  null);
                }
            }
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                //获取方法的名称
                String methodName = method.getName();
                //判断是否是studentDao中的get方法,
                if(methodName.startsWith("get")&& !methodName.startsWith("getClass")) {
                    Type genericReturnType = method.getGenericReturnType();
                    //获取实际返回的参数名
                    String returnTypeName = genericReturnType.getTypeName();
                    if (returnTypeName.contains("java.util.List")){
                        for (String key : map.keySet()) {
                            String substring = methodName.substring(3);
                            String uncapitalize = StringUtils.uncapitalize(substring);
                            if (uncapitalize.equals(key)){
                                int i = returnTypeName.indexOf("<")+1;
                                int i2 = returnTypeName.indexOf(">");
                                String s = returnTypeName.substring(i, i2);
                                map.put(key, Class.forName(s));
                            }
                        }
                    }
                }
            }
            //创建字段元数据
            FieldsMetadata fm = report.createFieldsMetadata();
            for (String k : map.keySet()) {
                Class<?> v = map.get(k);
                fm.load(k, v, true);
            }
            //Word模板中的表格数据对应的集合类型
            response.setCharacterEncoding("utf-8");
            response.setContentType("application/msword");
            String fileName = title + ".docx";
            response.setHeader("Content-Disposition" , "attachment;filename="
                    .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
            report.process(context, response.getOutputStream());
        } catch (IOException e) {
            log.error("IOException报错" , e);
        } catch (XDocReportException e) {
            log.error("XDocReportException报错" , e);
        } catch (Exception e) {
            log.error("Exception" , e);
        }
    }

}
相关推荐
非优秀程序员4 小时前
第04章 邮箱大战,163 与 QQ 的暗战|《互联网产品恩仇录》
java·vue.js·架构
知已难寻5 小时前
Docker 入门:容器技术与 Docker 基础
java·运维·服务器·docker·云原生·容器
文丑颜不良啊7 小时前
LeetCode2481~2500题目解析
java·leetcode
Flynn Liang8 小时前
同一需求三套写法:用 Spring AI、LangChain4j、AgentScope Java 各实现一个带工具调用的 RAG 助手(实战对比)
java·人工智能·大模型·架构设计·spring ai·langchain4j·agentscope
聪明的笨猪猪8 小时前
实际工程问题:AI 生成 Spring Cache(@Cacheable / @CachePut / @CacheEvict)缓存注解踩坑
java·经验分享·面试·ai编程
愿有所息9 小时前
记一次 ARM64 + JDK8 上 fastjson2 的诡异数据错乱
java
秋饼9 小时前
MCP 2026-07-28 无状态协议大改版:Spring AI 2.1 多轮往返请求与网关路由落地
java·ai·技术分享·后端开发
SL_staff10 小时前
JVS-BI 实战:业务人员如何用零代码 ELT + 可视化配置构建实时经营分析闭环
java·spring·全栈
SL_staff11 小时前
企业AI落地难?JVS-AI渐进式实践:安全可控、小切口验证、标准化沉淀与规模化复制
java·开源·aigc
SL_staff12 小时前
JVS数字底座实践:构建企业级统一内容管理平台的技术路径
java·产品