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);
        }
    }

}
相关推荐
海兰10 小时前
使用 Spring AI 打造企业级 RAG 知识库第二部分:AI 实战
java·人工智能·spring
历程里程碑10 小时前
二叉树---二叉树的中序遍历
java·大数据·开发语言·elasticsearch·链表·搜索引擎·lua
小信丶10 小时前
Spring Cloud Stream EnableBinding注解详解:定义、应用场景与示例代码
java·spring boot·后端·spring
无限进步_10 小时前
【C++】验证回文字符串:高效算法详解与优化
java·开发语言·c++·git·算法·github·visual studio
亚历克斯神10 小时前
Spring Cloud 2026 架构演进
java·spring·微服务
七夜zippoe10 小时前
Spring Cloud与Dubbo架构哲学对决
java·spring cloud·架构·dubbo·配置中心
海派程序猿10 小时前
Spring Cloud Config拉取配置过慢导致服务启动延迟的优化技巧
java
阿维的博客日记11 小时前
为什么不逃逸代表不需要锁,JIT会直接删掉锁
java
William Dawson11 小时前
CAS的底层实现
java
九英里路11 小时前
cpp容器——string模拟实现
java·前端·数据结构·c++·算法·容器·字符串