项目中多多少少会用到导出word这样便于导出后进行细微的手动修改。目前我没找到一款插件可以引入直接导出word、直接用最原始的方法poi导出word根据模板方法,这个导出需要用4.0以上版本才可以。
1、项目中引入依赖我的项目用的jdk1.8
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.0</version>
</dependency>
2、Controller
XWPFDocument doc = 处理类方法();
response.setContentType("multipart/form-data");
response.setHeader("Content-Disposition", "attachment;filename="导出word名称.docx", "utf-8"));
OutputStream outputStream = response.getOutputStream();
doc.write(outputStream);
outputStream.close();
3、编写处理方法serviceImpl
Resource resource = new ClassPathResource("wordTemplate.docx");//读取模板文件
InputStream input = resource.getInputStream();
doc = new XWPFDocument(input);
doParagraphs(doc, 参数);// 处理段落文字数据、包括文字和表格
doCharts0(doc, 参数); // 处理图表数据,柱状图、折线图、饼图啊之类的
4、处理文字表格等方法doParagraphs()
public void doParagraphs(XWPFDocument doc, 参数) {
try {
List<XWPFParagraph> paragraphList = doc.getParagraphs();
if (!CollectionUtils.isEmpty(paragraphList)) {
for (XWPFParagraph paragraph : paragraphList) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String text = run.getText(0);
if (text != null) {
if (text.contains("edittime")) {
run.setText(CommonConst.getnewTime(), 0); //替换文本信息
} else if(text.contains("${table33}")){
//表格生成
run.setText("", 0);
XmlCursor cursor = paragraph.getCTP().newCursor();
XWPFTable tableOne = doc.insertNewTbl(cursor);// ---这个是关键
// 设置表格宽度,第一行宽度就可以了,这个值的单位,目前我也还不清楚,还没来得及研究
tableOne.setWidth(10000);
// 表格第一行,对于每个列,必须使用createCell(),而不是getCell(),因为第一行嘛,肯定是属于创建的,没有create哪里来的get呢
XWPFTableRow tableOneRowOne = tableOne.getRow(0);//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "10%", "序号");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "30%", "企业名称");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "15%", "区域");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "15%", "行业");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "15%", "联系人");
new PoiWordTools().setWordCellSelfStyle(tableOneRowOne.createCell(), "微软雅黑", "9", 0, "left", "top", "#000000", "#B4C6E7", "15%", "联系电话");
List<Map<String, Object>> list = 获取的列表值方法;
if (!CollectionUtils.isEmpty(list)) {
for (int i = 0; i < list.size(); i++) {
Map<String, Object> Vmap = list.get(i);
// 表格第二行开始
XWPFTableRow tableOneRowTwo = tableOne.createRow();//行
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(0), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "10%", String.valueOf(i + 1));
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(1), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "30%", String.valueOf(Vmap.get("company_name")));
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(2), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "15%", String.valueOf(Vmap.get("street")));
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(3), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "15%", String.valueOf(Vmap.get("industry")));
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(4), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "15%", String.valueOf(Vmap.get("contact")));
new PoiWordTools().setWordCellSelfStyle(tableOneRowTwo.getCell(5), "微软雅黑", "9", 0, "left", "top", "#000000", "#ffffff", "15%", String.valueOf(Vmap.get("phone")));
}
}
CTVMerge vmergeStart = CTVMerge.Factory.newInstance();
vmergeStart.setVal(STMerge.RESTART);
CTVMerge vmergeEnd = CTVMerge.Factory.newInstance();
vmergeEnd.setVal(STMerge.CONTINUE);
}
}
}
}
}
} catch (Exception e) {
log.error("导出文本表格异常:" + e);
}
}
5、处理图表状图、条形图等方法doCharts0(doc, 参数)单个数据折线图
/**
* 折线图生成
*/
public static void doCharts0(XWPFDocument doc, 参数) {
List<Map<String, Object>> totalElectricityMap = null;
try {
JSONArray totalElectricityArr = 获取数据参数;
if (!CollectionUtils.isEmpty(totalElectricityArr)) {
totalElectricityMap = new ArrayList<>();
for (Object obj : totalElectricityArr) {
JSONObject o = JSONUtil.parseObj(obj);
Map<String, Object> map = new HashMap<>();
map.put("time", o.getStr("time"));
map.put("totalElectricity", o.getStr("totalElectricity"));
totalElectricityMap.add(map);
}
//获取word中所有图表对象
List<XWPFChart> charts = doc.getCharts();
//获取第一个单系列柱状图
XWPFChart multiLineChar = charts.get(1);
//系列信息
String[] multiLineSeriesNames = {"用电量(KW.h)"};
//分类信息
String[] multiLineCats = new String[totalElectricityMap.size()];
Number[] ElectricityValue = new Number[totalElectricityMap.size()];
for (int i = 0; i < totalElectricityMap.size(); i++) {
String time = String.valueOf(totalElectricityMap.get(i).get("time"));
multiLineCats[i] = time;//横轴x轴数据
ElectricityValue[i] = Double.valueOf(String.valueOf(totalElectricityMap.get(i).get("totalElectricity")));//纵轴Y轴数据
}
List<Number[]> multiLineValues = new ArrayList<>();
multiLineValues.add(ElectricityValue);
wordExportChar(multiLineChar, "用电曲线图", multiLineSeriesNames, multiLineCats, multiLineValues);
}
} catch (Exception e) {
log.error(e.toString());
}
}
5、多个数据柱状图其他类似
public void doCharts1(XWPFDocument doc, 参数) {
try {
List<Map<String, Object>> totalElectricityMap = new ArrayList<>();
if (!CollectionUtils.isEmpty(totalElectricityMap)) {
//获取word中所有图表对象
List<XWPFChart> charts = doc.getCharts();
//获取第一个单系列柱状图
XWPFChart multiLineChar = charts.get(2);
//系列信息
String[] multiLineSeriesNames = {"工作时间", "停机时间"};
//分类信息
String[] multiLineCats = new String[totalElectricityMap.size()];
Number[] workValue = new Number[totalElectricityMap.size()];
Number[] stopValue = new Number[totalElectricityMap.size()];
for (int i = 0; i < totalElectricityMap.size(); i++) {
String name = String.valueOf(totalElectricityMap.get(i).get("name"));
multiLineCats[i] = name;
workValue[i] = Double.valueOf(String.valueOf(totalElectricityMap.get(i).get("work")));
stopValue[i] = Double.valueOf(String.valueOf(totalElectricityMap.get(i).get("stop")));
}
List<Number[]> multiLineValues = new ArrayList<>();
multiLineValues.add(workValue);
multiLineValues.add(stopValue);
wordExportChar(multiLineChar, "企业生产用电与停产时间占比(%)", multiLineSeriesNames, multiLineCats, multiLineValues);
}
} catch (Exception e) {
log.error("e.toString());
}
}
6、公用方法处理类(处理图表的实际上就是往word的内嵌excel写数据然后刷新表格生成图表)
/**
* 根据word模板导出 针对图表(柱状图,折线图,饼图等)的处理
*
* @param docChart 图表对象
* @param title 图表标题
* @param seriesNames 系列名称数组
* @param cats 分类信息数组
* @param values 值信息集合 与系列数组一一对应
* @return {@link XWPFChart}
* @author LCheng
* @date 2020/12/10 11:08
*/
public static XWPFChart wordExportChar(XWPFChart docChart, String title, String[] seriesNames, String[] cats, List<Number[]> values) {
//获取图表数据对象
XDDFChartData chartData = docChart.getChartSeries().get(0);
//word图表均对应一个内置的excel,用于保存图表对应的数据
//excel中 第一列第二行开始的数据为分类信息
//CellRangeAddress(1, categories.size(), 0, 0) 四个参数依次为 起始行 截止行 起始列 截止列。
//excel中分类信息的范围
String catDataRange = docChart.formatRange(new CellRangeAddress(1, cats.length, 0, 0));
//根据分类信息的范围创建分类信息的数据源
XDDFDataSource<?> catDataSource = XDDFDataSourcesFactory.fromArray(cats, catDataRange, 0);
//更新数据
for (int i = 0; i < seriesNames.length; i++) {
//excel中各系列对应的数据的范围
String valDataRange = docChart.formatRange(new CellRangeAddress(1, cats.length, i + 1, i + 1));
//根据数据的范围创建值的数据源
Number[] val = values.get(i);
XDDFNumericalDataSource<Number> valDataSource = XDDFDataSourcesFactory.fromArray(val, valDataRange, i + 1);
//获取图表系列的数据对象
XDDFChartData.Series series = chartData.getSeries().get(i);
//替换系列数据对象中的分类和值
series.replaceData(catDataSource, valDataSource);
//修改系列数据对象中的标题
CellReference cellReference = docChart.setSheetTitle(seriesNames[i], 1);
series.setTitle(seriesNames[i], cellReference);
}
//更新图表数据对象
docChart.plot(chartData);
//图表整体的标题 传空值则不替换标题
if (!Strings.isNullOrEmpty(title)) {
docChart.setTitleText(title);
docChart.setTitleOverlay(false);
}
return docChart;
}
7、但是开发组合图表就是有次坐标的图表时候就不能上面那样写了、组合图表的时候它认为主坐标里面的系列是一起的、次坐标里面的系列是一起的、我这边的做法是这样先拿出来主坐标里面的系列进行数据写入刷新图表然后再拿出来次坐标里面的系列进行插入数据进行数据刷新最后一个组合图表就生成了、说到底也就是这个组合图我进行了两次只有一个坐标的操作而已。
列入如下图表代码生成:
/**
* 这个单独用来做导出有次坐标的组合图 因为代码接口认为一个坐标里面的数据是一个整体有了次坐标就认为是另一个直线图了
* 所以需要先处理主坐标里面的系列然后刷新图表在处理次坐标里面的系列刷新图表
* 相当于做了两次图表数据插入
*
* @param docChart 图表对象
* @param title 图表标题
* @param seriesNames 系列名称数组
* @param cats 分类信息数组
* @param values 值信息集合 与系列数组一一对应
* @return {@link XWPFChart}
* @author LCheng
* @date 2020/12/10 11:08
*/
public static XWPFChart wordExportChar2(XWPFChart docChart, String title, String[] seriesNames, String[] cats, List<Number[]> values) {
//只适合主次坐标里面每个里面只有一个系列的图表生成
for (int i = 0; i < 2; i++) {
//获取图表数据对象
XDDFChartData chartData = docChart.getChartSeries().get(i);
//word图表均对应一个内置的excel,用于保存图表对应的数据
//excel中 第一列第二行开始的数据为分类信息
//CellRangeAddress(1, categories.size(), 0, 0) 四个参数依次为 起始行 截止行 起始列 截止列。
//excel中分类信息的范围
String catDataRange = docChart.formatRange(new CellRangeAddress(1, cats.length, 0, 0));
//根据分类信息的范围创建分类信息的数据源
XDDFDataSource<?> catDataSource = XDDFDataSourcesFactory.fromArray(cats, catDataRange, 0);
//更新数据
//for (int i = 1; i < 2; i++) {
//excel中各系列对应的数据的范围
String valDataRange = docChart.formatRange(new CellRangeAddress(1, cats.length, i + 1, i + 1));
//根据数据的范围创建值的数据源
Number[] val = values.get(i);
XDDFNumericalDataSource<Number> valDataSource = XDDFDataSourcesFactory.fromArray(val, valDataRange, i + 1);
//获取图表系列的数据对象如果单个坐标里面有多个系列这个地方需要轮询数据切结
XDDFChartData.Series series = chartData.getSeries().get(0);
//替换系列数据对象中的分类和值
series.replaceData(catDataSource, valDataSource);
//修改系列数据对象中的标题
CellReference cellReference = docChart.setSheetTitle(seriesNames[i], 1);
series.setTitle(seriesNames[i], cellReference);
//}
//更新图表数据对象
docChart.plot(chartData);
}
//图表整体的标题 传空值则不替换标题
if (!Strings.isNullOrEmpty(title)) {
docChart.setTitleText(title);
docChart.setTitleOverlay(false);
}
return docChart;
}
8、模板在后面需要自取暂时没找到上传附件的功能哎先截图吧、模板也好做就是在word里面插入图表