声明:文章参考 【java 导出word文档 - CSDN App】http://t.csdnimg.cn/WcaP7
1.准备模板
对于表格的域需要带个自定义的前缀,如下
2.导入maven依赖
<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.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.16</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.14</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
3.导入工具类
package io.renren.modules.recycle.api.utils;
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 io.renren.modules.recycle.api.utils;
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());
}
}
}
package io.renren.modules.recycle.api.utils;
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);
}
}
4.导入表格内变量实体类
package io.renren.modules.recycle.api.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @Author: 表格变量实体类
* @Date: 2023/11/9 13:47
*/
@Data
public class ReportVo implements Serializable {
private static final long serialVersionUID = 1L;
private String profession;
private String topic;
}
5.数据库实体类
package io.renren.modules.recycle.api.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@Data
@TableName("report")
public class ReportEntity {
private Integer id;
private Integer userId;
// 专业
private String profession;
// 题目
private String topic;
// 中文摘要
private String abstractCn;
// 英文摘要
private String abstractEn;
//中文关键词
private String keywordCn;
//英文关键词
private String keywordEn;
}
6.service层
package io.renren.modules.recycle.api.service;
public interface ReportService {
public byte[] getSQLWordByte(Integer id);
}
serviceImpl
package io.renren.modules.recycle.api.service.impl;
import io.renren.modules.recycle.api.dao.ReportDao;
import io.renren.modules.recycle.api.dto.ReportVo;
import io.renren.modules.recycle.api.entity.ReportEntity;
import io.renren.modules.recycle.api.service.ReportService;
import io.renren.modules.recycle.api.utils.ExportData;
import io.renren.modules.recycle.api.utils.SoMap;
import io.renren.modules.recycle.api.utils.WordUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
@Service
public class ReportServiceImpl implements ReportService {
/**
* 模型路径
*/
@Value("${report.model-path-day}")
private String MODEL_PATH_DAY;
@Resource
private ReportDao reportDao;
@Override
public byte[] getSQLWordByte(Integer id) {
//模拟从数据库 根据id 查数据然后插入
final ReportEntity reportEntity = reportDao.selectById(id);
if (reportEntity==null){
return null;
}
//准备数据
ExportData evaluation = WordUtil.createExportData(MODEL_PATH_DAY);
//填充数据
evaluation.setData("profession",reportEntity.getProfession());
evaluation.setData("topic",reportEntity.getTopic());
evaluation.setData("abstract",reportEntity.getAbstractCn());
evaluation.setData("abstractEn",reportEntity.getAbstractEn());
evaluation.setData("keyword",reportEntity.getKeywordCn());
evaluation.setData("keywordEn",reportEntity.getKeywordEn());
//填充表格里的专业和题目
final ReportVo reportVo = new ReportVo();
reportVo.setProfession(reportEntity.getProfession());
reportVo.setTopic(reportEntity.getTopic());
final ArrayList<SoMap> videoList = new ArrayList<SoMap>();
videoList.add(new SoMap(reportVo));
//创建word模板时自定义的表格前缀
evaluation.setTable("video",videoList);
//获取新生成的文件流
byte[] data = evaluation.getByteArr();
return data;
}
}
7.controller层
package io.renren.modules.recycle.api.controller;
import io.renren.modules.recycle.api.service.ReportService;
import io.renren.modules.recycle.api.utils.ResUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.UUID;
@RestController
@RequestMapping("/app/recycle/report")
@Slf4j
public class ReportController {
@Resource
private ReportService reportService;
@GetMapping("/sql/export")
public Object exportSQL(HttpServletRequest request) {
//根据文章id从数据库查询,然后导出word
//这里测试用,所以写死id为1,实际应该是接口传入 id
byte[] data = reportService.getSQLWordByte(1);
return ResUtil.getStreamData(request,data,UUID.randomUUID().toString(),"docx");
}
}
8.yml配置,把word模板放置在指定文件夹里,并配置yml
report:
model-path-day: "D://ceshi.docx" # word模板地址
9.打开浏览器访问自己的接口地址