java导出word的5种方式
1:Jacob是Java-COM Bridge的缩写
它在Java与微软的COM组件之间构建一座桥梁。通过Jacob实现了在Java平台上对微软Office的COM接口进行调用。
优点:调用微软Office的COM接口,生成的word文件格式规范。
缺点:服务器只能是windows平台,不支持unix和linux,且服务器上必须安装微软Office。
2:Apache POI
它们可以操作基于MicroSoft OLE 2 Compound Document Format的各种格式文件,可以通过这些API在Java中读写Excel、Word等文件。
优点:跨平台支持windows、unix和linux。
缺点:相对与对word文件的处理来说,POI更适合excel处理,对于word实现一些简单文件的操作凑合,不能设置样式且生成的word文件格式不够规范。
3:Java2word是一个在java程序中调用 MS Office Word 文档的组件(类库)。
该组件提供了一组简单的接口,以便java程序调用他的服务操作Word 文档。 这些服务包括: 打开文档、新建文档、查找文字、替换文字,插入文字、插入图片、插入表格,在书签处插入文字、插入图片、插入表格等。
优点:足够简单,操作起来要比FreeMarker简单的多。
缺点:没有FreeMarker强大,不能够根据模版生成Word文档,word的文档的样式等信息都不能够很好的操作。
4:FreeMarker生成word文档的功能是由XML+FreeMarker来实现的。
先把word文件另存为xml,在xml文件中插入特殊的字符串占位符,将xml翻译为FreeMarker模板,最后用java来解析FreeMarker模板,编码调用FreeMarker实现文本替换并输出Doc。
优点:比Java2word功能强大,也是纯Java编程。
缺点:生成的文件本质上是xml,不是真正的word文件格式,有很多常用的word格式无法处理或表现怪异,比如:超链、换行、乱码、部分生成的文件打不开等。
5:PageOffice生成word文件。
PageOffice封装了微软Office繁琐的vba接口,提供了简洁易用的Java编程对象,支持生成word文件,同时实现了在线编辑word文档和读取word文档内容。
优点:跨平台支持windows、unix和linux,生成word文件格式标准,支持文本、图片、表格、字体、段落、颜色、超链、页眉等各种格式的操作,支持多word合并,无需处理并发,不耗费服务器资源,运行稳定。
缺点:必须在客户端生成文件(可以不显示界面),不支持纯服务器端生成文件。
使用 apache poi + easypoi 导出word
1.需要的依赖
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-base</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-web</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>cn.afterturn</groupId>
<artifactId>easypoi-annotation</artifactId>
<version>4.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.1</version>
</dependency>
2.导出word模板
3.创建更新图表标题类 WordPoiTitle
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTTitle;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTTx;
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;
import java.util.List;
/**
* 更新图表的标题
*/
public class WordPoiTitle {
public void setBarTitle(CTChart ctChart, String title) {
CTTitle ctTitle = ctChart.getTitle();
CTTx ctTx = ctTitle.getTx();
if (null != ctTx) {
CTTextBody ctTextBody = ctTx.getRich();
List<CTTextParagraph> ctTextParagraphslist = ctTextBody.getPList();
CTTextParagraph ctTextParagraph1 = ctTextParagraphslist.get(0);
List<CTRegularTextRun> ctRegularTextRunslist = ctTextParagraph1.getRList();
ctRegularTextRunslist.get(0).setT(title); // 设置标题
}
}
}
4.POI生成word工具类 WordPoiTools
这个类里面有 折线图、饼图、区县图,根据需要调用 针对于模板中的图表是静态的,也就是模板中的图表长什么样子不会根据数据而改变
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.openxmlformats.schemas.drawingml.x2006.chart.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
/**
* poi生成word的工具类
* 针对于模板中的图表是静态的,也就是模板中的图表长什么样子不会根据数据而改变
*/
public class WordPoiTools {
private static final BigDecimal bd2 = new BigDecimal("2");
/**
* 调用替换雷达图数据
*/
public static void replaceRadarCharts(POIXMLDocumentPart poixmlDocumentPart,
List<String> titleArr, List<String> fldNameArr,
List<Map<String, String>> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
// 设置标题
// new PoiWordTitle().setBarTitle(ctChart, "我是雷达图标题");
CTRadarChart radarChart = plotArea.getRadarChartArray(0);
List<CTRadarSer> radarList = radarChart.getSerList(); // 获取雷达图单位
//刷新内置excel数据
new WordPoiTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
refreshRadarStrGraphContent(radarChart, radarList, listItemsByType, fldNameArr, 1);
}
/**
* 调用替换柱状图数据
*/
public static void replaceBarCharts(POIXMLDocumentPart poixmlDocumentPart,
List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
// 设置标题
new WordPoiTitle().setBarTitle(ctChart, "我是修改后的标题");
CTBarChart barChart = plotArea.getBarChartArray(0);
List<CTBarSer> BarSerList = barChart.getSerList(); // 获取柱状图单位
//刷新内置excel数据
new WordPoiTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
refreshBarStrGraphContent(barChart, BarSerList, listItemsByType, fldNameArr, 1);
}
/**
* 调用替换折线图数据
*/
public static void replaceLineCharts(POIXMLDocumentPart poixmlDocumentPart,
List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTLineChart lineChart = plotArea.getLineChartArray(0);
List<CTLineSer> lineSerList = lineChart.getSerList(); // 获取折线图单位
//刷新内置excel数据
new WordPoiTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
new WordPoiTools().refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 1);
}
/**
* 调用替换饼图数据
*/
public static void replacePieCharts(POIXMLDocumentPart poixmlDocumentPart,
List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTPieChart pieChart = plotArea.getPieChartArray(0);
List<CTPieSer> pieSerList = pieChart.getSerList(); // 获取饼图单位
//刷新内置excel数据
new WordPoiTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
new WordPoiTools().refreshPieStrGraphContent(pieChart, pieSerList, listItemsByType, fldNameArr, 1);
}
/**
* 调用替换柱状图、折线图组合数据
*/
public static void replaceCombinationCharts(POIXMLDocumentPart poixmlDocumentPart,
List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTBarChart barChart = plotArea.getBarChartArray(0);
List<CTBarSer> barSerList = barChart.getSerList(); // 获取柱状图单位
//刷新内置excel数据
new WordPoiTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据 数据中下标1开始的是柱状图数据,所以这个是1
refreshBarStrGraphContent(barChart, barSerList, listItemsByType, fldNameArr, 1);
CTLineChart lineChart = plotArea.getLineChartArray(0);
List<CTLineSer> lineSerList = lineChart.getSerList(); // 获取折线图单位
//刷新内置excel数据 有一个就可以了 有一个就可以了 有一个就可以了
//new PoiWordTools().refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据 数据中下标3开始的是折线图的数据,所以这个是3
new WordPoiTools().refreshLineStrGraphContent(lineChart, lineSerList, listItemsByType, fldNameArr, 3);
}
/**
* 刷新折线图数据方法
*
* @param typeChart
* @param serList
* @param dataList
* @param fldNameArr
* @param position
* @return
*/
public static boolean refreshLineStrGraphContent(Object typeChart,
List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {
boolean result = true;
//更新数据区域
for (int i = 0; i < serList.size(); i++) {
CTAxDataSource cat = null;
CTNumDataSource val = null;
CTLineSer ser = ((CTLineChart) typeChart).getSerArray(i);
// 设置标题
CTSerTx tx = ser.getTx();
tx.getStrRef().getStrCache().getPtList().get(0).setV("阿里嘎痛"); // wps和office都能打开
// Category Axis Data
cat = ser.getCat();
// 获取图表的值
val = ser.getVal();
// strData.set
CTStrData strData = cat.getStrRef().getStrCache();
CTNumData numData = val.getNumRef().getNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.getPtCount().setVal(idx);
strData.getPtCount().setVal(idx);
//赋值横坐标数据区域
String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
.formatAsString("Sheet1", false);
cat.getStrRef().setF(axisDataRange);
//数据区域
String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
.formatAsString("Sheet1", false);
val.getNumRef().setF(numDataRange);
// 设置系列生成方向
}
return result;
}
/**
* 刷新柱状图数据方法
*
* @param typeChart
* @param serList
* @param dataList
* @param fldNameArr
* @param position
* @return
*/
public static boolean refreshBarStrGraphContent(Object typeChart,
List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {
boolean result = true;
//更新数据区域
for (int i = 0; i < serList.size(); i++) {
CTAxDataSource cat = null;
CTNumDataSource val = null;
CTBarSer ser = ((CTBarChart) typeChart).getSerArray(i);
// 设置标题 用以下这个方式,可以兼容office和wps
CTSerTx tx = ser.getTx();
// tx.getStrRef().getStrCache().getPtList().get(0).setV("嘿嘿嘿");
// Category Axis Data
cat = ser.getCat();
// 获取图表的值
val = ser.getVal();
// strData.set
CTStrData strData = cat.getStrRef().getStrCache();
CTNumData numData = val.getNumRef().getNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.getPtCount().setVal(idx);
strData.getPtCount().setVal(idx);
//赋值横坐标数据区域
String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
.formatAsString("Sheet1", true);
cat.getStrRef().setF(axisDataRange);
//数据区域
String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
.formatAsString("Sheet1", true);
val.getNumRef().setF(numDataRange);
}
return result;
}
/**
* 刷新饼图数据方法
*
* @param typeChart
* @param serList
* @param dataList
* @param fldNameArr
* @param position
* @return
*/
public static boolean refreshPieStrGraphContent(Object typeChart,
List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {
boolean result = true;
//更新数据区域
for (int i = 0; i < serList.size(); i++) {
//CTSerTx tx=null;
CTAxDataSource cat = null;
CTNumDataSource val = null;
CTPieSer ser = ((CTPieChart) typeChart).getSerArray(i);
//tx.getStrRef().getStrCache().getPtList().get(0).setV("阿里嘎痛");
// Category Axis Data
cat = ser.getCat();
// 获取图表的值
val = ser.getVal();
// strData.set
CTStrData strData = cat.getStrRef().getStrCache();
CTNumData numData = val.getNumRef().getNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
numData.getPtCount().setVal(idx);
strData.getPtCount().setVal(idx);
//赋值横坐标数据区域
String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
.formatAsString("Sheet1", true);
cat.getStrRef().setF(axisDataRange);
//数据区域
String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
.formatAsString("Sheet1", true);
val.getNumRef().setF(numDataRange);
}
return result;
}
/**
* 刷新内置excel数据
*
* @param chart
* @param dataList
* @param fldNameArr
* @param titleArr
* @return
*/
public static boolean refreshExcel(XWPFChart chart,
List<Map<String, String>> dataList, List<String> fldNameArr, List<String> titleArr) {
boolean result = true;
Workbook wb = new XSSFWorkbook();
Sheet sheet = wb.createSheet("Sheet1");
//根据数据创建excel第一行标题行
for (int i = 0; i < titleArr.size(); i++) {
if (sheet.getRow(0) == null) {
sheet.createRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
} else {
sheet.getRow(0).createCell(i).setCellValue(titleArr.get(i) == null ? "" : titleArr.get(i));
}
}
//遍历数据行
for (int i = 0; i < dataList.size(); i++) {
Map<String, String> baseFormMap = dataList.get(i);//数据行
//fldNameArr字段属性
for (int j = 0; j < fldNameArr.size(); j++) {
if (sheet.getRow(i + 1) == null) {
if (j == 0) {
try {
sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)) == null ? "" : baseFormMap.get(fldNameArr.get(j)));
} catch (Exception e) {
if (baseFormMap.get(fldNameArr.get(j)) == null) {
sheet.createRow(i + 1).createCell(j).setCellValue("");
} else {
sheet.createRow(i + 1).createCell(j).setCellValue(baseFormMap.get(fldNameArr.get(j)));
}
}
}
} else {
BigDecimal b = new BigDecimal(baseFormMap.get(fldNameArr.get(j)));
double value = 0d;
if (b != null) {
value = b.doubleValue();
}
if (value == 0) {
sheet.getRow(i + 1).createCell(j);
} else {
sheet.getRow(i + 1).createCell(j).setCellValue(b.doubleValue());
}
}
}
}
// 更新嵌入的workbook
POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
OutputStream xlsOut = xlsPart.getPackagePart().getOutputStream();
try {
wb.write(xlsOut);
xlsOut.close();
} catch (IOException e) {
e.printStackTrace();
result = false;
} finally {
if (wb != null) {
try {
wb.close();
} catch (IOException e) {
e.printStackTrace();
result = false;
}
}
}
return result;
}
/**
* 设置表格样式
*
* @param cell
* @param fontName
* @param fontSize
* @param fontBlod
* @param alignment
* @param vertical
* @param fontColor
* @param bgColor
* @param cellWidth
* @param content
*/
public static void setWordCellSelfStyle(XWPFTableCell cell, String fontName, String fontSize, int fontBlod,
String alignment, String vertical, String fontColor,
String bgColor, String cellWidth, String content) {
//poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理
BigInteger bFontSize = new BigInteger("24");
if (fontSize != null && !fontSize.equals("")) {
//poi对字体大小设置特殊,不支持小数,但对原word字体大小做了乘2处理
BigDecimal fontSizeBD = new BigDecimal(fontSize);
fontSizeBD = bd2.multiply(fontSizeBD);
fontSizeBD = fontSizeBD.setScale(0, BigDecimal.ROUND_HALF_UP);//这里取整
bFontSize = new BigInteger(fontSizeBD.toString());// 字体大小
}
// 设置单元格宽度
cell.setWidth(cellWidth);
//=====获取单元格
CTTc tc = cell.getCTTc();
//====tcPr开始====》》》》
CTTcPr tcPr = tc.getTcPr();//获取单元格里的<w:tcPr>
if (tcPr == null) {//没有<w:tcPr>,创建
tcPr = tc.addNewTcPr();
}
// --vjc开始-->>
CTVerticalJc vjc = tcPr.getVAlign();//获取<w:tcPr> 的<w:vAlign w:val="center"/>
if (vjc == null) {//没有<w:w:vAlign/>,创建
vjc = tcPr.addNewVAlign();
}
//设置单元格对齐方式
vjc.setVal(vertical.equals("top") ? STVerticalJc.TOP : vertical.equals("bottom") ? STVerticalJc.BOTTOM : STVerticalJc.CENTER); //垂直对齐
CTShd shd = tcPr.getShd();//获取<w:tcPr>里的<w:shd w:val="clear" w:color="auto" w:fill="C00000"/>
if (shd == null) {//没有<w:shd>,创建
shd = tcPr.addNewShd();
}
// 设置背景颜色
shd.setFill(bgColor.substring(1));
//《《《《====tcPr结束====
//====p开始====》》》》
CTP p = tc.getPList().get(0);//获取单元格里的<w:p w:rsidR="00C36068" w:rsidRPr="00B705A0" w:rsidRDefault="00C36068" w:rsidP="00C36068">
//---ppr开始--->>>
CTPPr ppr = p.getPPr();//获取<w:p>里的<w:pPr>
if (ppr == null) {//没有<w:pPr>,创建
ppr = p.addNewPPr();
}
// --jc开始-->>
CTJc jc = ppr.getJc();//获取<w:pPr>里的<w:jc w:val="left"/>
if (jc == null) {//没有<w:jc/>,创建
jc = ppr.addNewJc();
}
//设置单元格对齐方式
jc.setVal(alignment.equals("left") ? STJc.LEFT : alignment.equals("right") ? STJc.RIGHT : STJc.CENTER); //水平对齐
// <<--jc结束--
// --pRpr开始-->>
CTParaRPr pRpr = ppr.getRPr(); //获取<w:pPr>里的<w:rPr>
if (pRpr == null) {//没有<w:rPr>,创建
pRpr = ppr.addNewRPr();
}
CTFonts pfont = pRpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体"/>
if (pfont == null) {//没有<w:rPr>,创建
pfont = pRpr.addNewRFonts();
}
//设置字体
pfont.setAscii(fontName);
pfont.setEastAsia(fontName);
pfont.setHAnsi(fontName);
CTOnOff pb = pRpr.getB();//获取<w:rPr>里的<w:b/>
if (pb == null) {//没有<w:b/>,创建
pb = pRpr.addNewB();
}
//设置字体是否加粗
pb.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);
CTHpsMeasure psz = pRpr.getSz();//获取<w:rPr>里的<w:sz w:val="32"/>
if (psz == null) {//没有<w:sz w:val="32"/>,创建
psz = pRpr.addNewSz();
}
// 设置单元格字体大小
psz.setVal(bFontSize);
CTHpsMeasure pszCs = pRpr.getSzCs();//获取<w:rPr>里的<w:szCs w:val="32"/>
if (pszCs == null) {//没有<w:szCs w:val="32"/>,创建
pszCs = pRpr.addNewSzCs();
}
// 设置单元格字体大小
pszCs.setVal(bFontSize);
// <<--pRpr结束--
//<<<---ppr结束---
//---r开始--->>>
List<CTR> rlist = p.getRList(); //获取<w:p>里的<w:r w:rsidRPr="00B705A0">
CTR r = null;
if (rlist != null && rlist.size() > 0) {//获取第一个<w:r>
r = rlist.get(0);
} else {//没有<w:r>,创建
r = p.addNewR();
}
//--rpr开始-->>
CTRPr rpr = r.getRPr();//获取<w:r w:rsidRPr="00B705A0">里的<w:rPr>
if (rpr == null) {//没有<w:rPr>,创建
rpr = r.addNewRPr();
}
//->-
CTFonts font = rpr.getRFonts();//获取<w:rPr>里的<w:rFonts w:ascii="宋体" w:eastAsia="宋体" w:hAnsi="宋体" w:hint="eastAsia"/>
if (font == null) {//没有<w:rFonts>,创建
font = rpr.addNewRFonts();
}
//设置字体
font.setAscii(fontName);
font.setEastAsia(fontName);
font.setHAnsi(fontName);
CTOnOff b = rpr.getB();//获取<w:rPr>里的<w:b/>
if (b == null) {//没有<w:b/>,创建
b = rpr.addNewB();
}
//设置字体是否加粗
b.setVal(fontBlod == 1 ? STOnOff.ON : STOnOff.OFF);
CTColor color = rpr.getColor();//获取<w:rPr>里的<w:color w:val="FFFFFF" w:themeColor="background1"/>
if (color == null) {//没有<w:color>,创建
color = rpr.addNewColor();
}
// 设置字体颜色
if (content.contains("↓")) {
color.setVal("43CD80");
} else if (content.contains("↑")) {
color.setVal("943634");
} else {
color.setVal(fontColor.substring(1));
}
CTHpsMeasure sz = rpr.getSz();
if (sz == null) {
sz = rpr.addNewSz();
}
sz.setVal(bFontSize);
CTHpsMeasure szCs = rpr.getSzCs();
if (szCs == null) {
szCs = rpr.addNewSz();
}
szCs.setVal(bFontSize);
//-<-
//<<--rpr结束--
List<CTText> tlist = r.getTList();
CTText t = null;
if (tlist != null && tlist.size() > 0) {//获取第一个<w:r>
t = tlist.get(0);
} else {//没有<w:r>,创建
t = r.addNewT();
}
t.setStringValue(content);
//<<<---r结束---
}
/**
* 获取内置表格数据,拿到第一行第一列格子数据
* 有时候模板设计太复杂,对于图表不能精准定位,可以通过设置图表表格数据的第一行第一列格子数据来区分,这个数据不影响图表显示,所以用来区分每个图表
*/
public static String getZeroData(POIXMLDocumentPart poixmlDocumentPart) {
String text = "";
try {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
POIXMLDocumentPart xlsPart = chart.getRelations().get(0);
InputStream xlsin = xlsPart.getPackagePart().getInputStream();
Workbook workbook = new XSSFWorkbook(xlsin);
// 获取第一个sheet
Sheet sheet = workbook.getSheetAt(0);
// 第一行
Row row = sheet.getRow(0);
// 第一列
Cell cell = row.getCell(0);
cell.setCellType(CellType.STRING); // 设置一下格子类型为字符串,不然如果是数字或者时间的话,获取很麻烦
text = cell.getStringCellValue(); // 获取格子内容
System.out.println("(0,0)格子值:" + text);
// 关闭流
xlsin.close();
} catch (Exception e) {
e.printStackTrace();
}
return text;
}
/**
* 刷新雷达图数据方法
*
* @param typeChart
* @param serList
* @param dataList
* @param fldNameArr
* @param position
* @return
*/
public static boolean refreshRadarStrGraphContent(Object typeChart,
List<?> serList, List<Map<String, String>> dataList, List<String> fldNameArr, int position) {
boolean result = true;
//更新数据区域
for (int i = 0; i < serList.size(); i++) {
CTAxDataSource cat = null;
CTNumDataSource val = null;
CTRadarSer ser = ((CTRadarChart) typeChart).getSerArray(i);
// 设置标题 用以下这个方式,可以兼容office和wps
CTSerTx tx = ser.getTx();
// tx.getStrRef().getStrCache().getPtList().get(0).setV("嘿嘿嘿");
// Category Axis Data
cat = ser.getCat();
// 获取图表的值
val = ser.getVal();
// strData.set
if (null != cat.getNumRef()) {
cat.unsetNumRef();
}
if (null != cat.getStrRef()) {
cat.unsetStrRef();
}
cat.addNewStrRef().addNewStrCache();
CTStrData strData = cat.getStrRef().getStrCache();
CTNumData numData = val.getNumRef().getNumCache();
strData.setPtArray((CTStrVal[]) null); // unset old axis text
numData.setPtArray((CTNumVal[]) null); // unset old values
// set model
long idx = 0;
for (int j = 0; j < dataList.size(); j++) {
//判断获取的值是否为空
String value = "0";
if (new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))) != null) {
value = new BigDecimal(dataList.get(j).get(fldNameArr.get(i + position))).toString();
}
if (!"0".equals(value)) {
CTNumVal numVal = numData.addNewPt();//序列值
numVal.setIdx(idx);
numVal.setV(value);
}
CTStrVal sVal = strData.addNewPt();//序列名称
sVal.setIdx(idx);
sVal.setV(dataList.get(j).get(fldNameArr.get(0)));
idx++;
}
strData.addNewPtCount().setVal(idx);
numData.getPtCount().setVal(idx);
//赋值横坐标数据区域
String axisDataRange = new CellRangeAddress(1, dataList.size(), 0, 0)
.formatAsString("Sheet1", true);
cat.getStrRef().setF(axisDataRange);
//数据区域
String numDataRange = new CellRangeAddress(1, dataList.size(), i + position, i + position)
.formatAsString("Sheet1", true);
val.getNumRef().setF(numDataRange);
}
return result;
}
}
5.创建 WordFileUtil 类
import cn.afterturn.easypoi.word.WordExportUtil;
import org.apache.poi.ooxml.POIXMLDocumentPart;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieChart;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPieSer;
import org.openxmlformats.schemas.drawingml.x2006.chart.CTPlotArea;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class WordFileUtil {
/**
* 根据模板导出Word
*/
public static void exportWordByModel(HttpServletResponse response, Map<String, Object> map, String modelFileName, String outFileName) {
try {
// 1.获取模板文件路径 - 重点
//XWPFDocument word = WordExportUtil.exportWord07(modelFileName, map);有时候这种方式可以找到有时候找不到(不太清楚)
String templatePath = filePath(modelFileName).getAbsolutePath();
// 打印出模板文件的完整路径 - 校验路径是否存在
File templateFile = new File(templatePath);
if (templateFile.exists()) {
System.out.println("模板文件存在: " + templateFile.getAbsolutePath());
} else {
System.out.println("模板文件不存在: " + templateFile.getAbsolutePath());
}
// 2.映射模板,替换数据
XWPFDocument word = WordExportUtil.exportWord07(templatePath, map);
// 3.设置返回参数的字符集
response.reset();
response.setHeader("Access-Control-Allow-Origin", "*");
response.setContentType("application/msexcel");
response.setContentType("text/html; charset=UTF-8");
// 4.设置响应类型为Word文档
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
// 5.中文文件名处理,否则报错
String encodedFileName = URLEncoder.encode(outFileName, "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + encodedFileName + ".docx");
// 6.将Word文档发送到浏览器
word.write(response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
}
}
public static File filePath(String modelFileName) {
// 获取类加载器
ClassLoader classLoader = WordFileUtil.class.getClassLoader();
// 尝试从类路径中加载资源
URL resource = classLoader.getResource(modelFileName);
return new File(resource.getFile());
}
public static void doCharts(List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType, XWPFDocument doc) throws FileNotFoundException {
// 获取word模板中的所有图表元素,用map存放
// 为什么不用list保存:查看doc.getRelations()的源码可知,源码中使用了hashMap读取文档图表元素,
// 对relations变量进行打印后发现,图表顺序和文档中的顺序不一致,也就是说relations的图表顺序不是文档中从上到下的顺序
Map<String, POIXMLDocumentPart> chartsMap = new HashMap<String, POIXMLDocumentPart>();
//动态刷新图表
List<POIXMLDocumentPart> relations = doc.getRelations();
for (POIXMLDocumentPart poixmlDocumentPart : relations) {
if (poixmlDocumentPart instanceof XWPFChart) { // 如果是图表元素
// 获取图表对应的表格数据里面的第一行第一列数据,可以拿来当作key值
String text = WordPoiTools.getZeroData(poixmlDocumentPart);
System.out.println(text);
String str = poixmlDocumentPart.toString();
System.out.println("str:" + str);
String key = str.replaceAll("Name: ", "")
.replaceAll(" - Content Type: application/vnd\\.openxmlformats-officedocument\\.drawingml\\.chart\\+xml", "").trim();
System.out.println("key:" + key);
chartsMap.put(key, poixmlDocumentPart);
}
}
//饼图
POIXMLDocumentPart poiXmlDocumentPart4 = chartsMap.get("/word/charts/chart1.xml");
replacePieCharts(poiXmlDocumentPart4, titleArr, fldNameArr, listItemsByType);
}
/**
* 调用替换饼图数据
*/
public static void replacePieCharts(POIXMLDocumentPart poixmlDocumentPart,
List<String> titleArr, List<String> fldNameArr, List<Map<String, String>> listItemsByType) {
XWPFChart chart = (XWPFChart) poixmlDocumentPart;
chart.getCTChart();
//根据属性第一列名称切换数据类型
CTChart ctChart = chart.getCTChart();
CTPlotArea plotArea = ctChart.getPlotArea();
CTPieChart pieChart = plotArea.getPieChartArray(0);
List<CTPieSer> pieSerList = pieChart.getSerList(); // 获取饼图单位
//刷新内置excel数据
WordPoiTools.refreshExcel(chart, listItemsByType, fldNameArr, titleArr);
//刷新页面显示数据
WordPoiTools.refreshPieStrGraphContent(pieChart, pieSerList, listItemsByType, fldNameArr, 1);
}
}
6.main 方法调用测试
public static void main(String[] args) {
String monthlyCont= "俗话说得好:一日之计在于晨!对于饮食来说,早餐的营养是否丰富,是否均衡,极为重要!" +
"老于记得小时候,谁家早餐能整半斤油条,一大海碗豆浆,一碟小咸菜,那就是一顿很奢侈的早餐了。但是,由于现在家家户户的生活水平都非常不错," +
"很多朋友不是营养欠缺,而是营养过剩,甚至,早餐的热量比肉还高,导致自己不知不觉就胖了很多!但是,这三种消耗能量的方式,老于认为慢跑最为适合," +
"因为10公里慢跑的也就是1个小时左右;如果是步行的话,至少是2个小时起步!但是,正常的跑步,很多朋友又坚持不了这么高强度的训练,关键是跑步比慢跑消耗的要低!";
Map<String, Object> textMap = new HashMap<>();
textMap.put("compName", "上海科技有限公司");
textMap.put("yyMonthDay", "2024年8月31日");
textMap.put("periods", "2024年第8期");
textMap.put("cont", monthlyCont);
// 数据准备
List<String> titleArr = new ArrayList<>();// 标题
titleArr.add("title");
titleArr.add("金额");
List<String> fldNameArr = new ArrayList<>();// 字段名
fldNameArr.add("item1");
fldNameArr.add("item2");
//数据集合
List<Map<String, String>> listItemsByType = new ArrayList<Map<String, String>>();
// 第一行数据
Map<String, String> base1 = new HashMap<String, String>();
base1.put("item1", "材料费用");
base1.put("item2", "500");
// 第二行数据
Map<String, String> base2 = new HashMap<String, String>();
base2.put("item1", "出差费用");
base2.put("item2", "300");
// 第三行数据
Map<String, String> base3 = new HashMap<>();
base3.put("item1", "住宿费用");
base3.put("item2", "300");
listItemsByType.add(base1);
listItemsByType.add(base2);
listItemsByType.add(base3);
try {
//模板文件
String templatePath = WordFileUtil.filePath("template_word/monthly.docx").getAbsolutePath();
XWPFDocument word = WordExportUtil.exportWord07(templatePath, textMap);
//图表
WordFileUtil.doCharts(titleArr, fldNameArr, listItemsByType, word);
// 结果文件
final String returnUrl = "D:\\work\\monthly_08.docx";
File file = new File(returnUrl);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = new FileOutputStream(returnUrl);
word.write(fos);
fos.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
//根据模板导出Word,此处前端导出功能用
//FileUtil.exportWordByModel(response,map,"templates/word.docx","员工统计");
}