前期准备工作word模板
右键字段如果无编辑域 ctrl+F9 一下,然后再右键
wps 直接 ctrl+F9 会变成编辑域
pom.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 层
@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 层
public interface WordService {
void export(response,query);
}
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
模板工具类
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);
}
}
}