java使用poi操作word插入图片、段落、表格
其他链接
java使用poi操作world生成饼图,柱状图,折线图,组合图:一
java使用poi操作world生成饼图,柱状图,折线图,组合图:二
问题链接
准备工作
创建word模板.docx文件
使用模板操作,因为自己使用poi去写word过于麻烦,耦合性太高
${XXX}理解成占位符,会替换为填充的数据,可配置为其他比如*{xxx}*
编写模板格式.xml文件
创建的.docx文件顺序基本是乱的,需要另存为.xml格式手动更改格式
用编译器打开(我用的IDEA)找到要替换的占位符的位置
把所有要替换的位置都找到改为一行
以此类推改完所有替换内容的标识
改完之后在改成.docx格式
java上手poi
maven依赖
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.16</version>
</dependency>
使用到的包
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.xwpf.usermodel.*;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.phantomjs.PhantomJSDriver;
import org.openqa.selenium.phantomjs.PhantomJSDriverService;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.text.DecimalFormat;
import java.util.*;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
具体应用
/**
* 读取模板
* @param HttpServletResponse
* @throws Exception
*/
public void operatorWord(HttpServletResponse response) throws Exception {
//获得模板文件
InputStream docis = new FileInputStream("C:\Users\16630\Desktop\模板A.docx");
//转成word
CustomXWPFDocument document = new CustomXWPFDocument(docis);
//写入图片
this.insertImage(document);
//写入表格
this.insertTable(document);
//段落替换对象
this.insertText(document)
//把doc输出到输出流
response.setCharacterEncoding("UTF-8");
response.setContentType("application/msword");
//文件名
String fileName = "考试成绩分析" + htmlDataTime.getStartData();
response.setHeader("Content-Disposition", "attachment;fileName="+ new String(fileName.getBytes("UTF-8"),"ISO-8859-1"));
ServletOutputStream responseOutputStream = response.getOutputStream();
document.write(responseOutputStream);
responseOutputStream.flush();
responseOutputStream.close();
in.close();
}
对应封装方法 ----------图片插入指定位置操作
/**
* 写入图片在word中
* @param document
* @throws IOException
* @throws InvalidFormatException
*/
private void insertImage(CustomXWPFDocument document) throws IOException, InvalidFormatException {
//图片
FileInputStream in = new FileInputStream(new("C:\Users\16630\Desktop\image.jpg"));
//段落集合
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
//获取到段落中的所有文本内容
String text = paragraph.getText();
//判断此段落中是否有需要进行替换的文本
if (WordUtil.checkText(text)) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
//替换模板原来位置
String key = "${image}";
if (run.toString().indexOf(key) != -1) {
byte[] ba = new byte[in.available()];
int len = in.read(ba);
ByteArrayInputStream byteInputStream = new ByteArrayInputStream(ba, 0, len);
//设置图片
document.addPictureData(byteInputStream, XWPFDocument.PICTURE_TYPE_PNG);
//创建一个word图片,并插入到文档中-->像素可改
document.createPicture(document.getAllPictures().size() - 1, 240, 240,paragraph);
}
break;
}
break;
}
}
}
图片操作使用到的工具类
/**
* @author BM_hyjw
*/
import java.io.IOException;
import java.io.InputStream;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
public class CustomXWPFDocument extends XWPFDocument{
public CustomXWPFDocument(InputStream in) throws IOException {
super(in);
}
public CustomXWPFDocument() {
super();
}
public CustomXWPFDocument(OPCPackage pkg) throws IOException {
super(pkg);
}
/**
* @param id
* @param width
* 宽
* @param height
* 高
* @param paragraph
* 段落
*/
public void createPicture(int id, int width, int height,
XWPFParagraph paragraph) {
final int EMU = 9525;
width *= EMU;
height *= EMU;
String blipId = super.getRelationId(super.getAllPictures().get(id));
CTInline inline = paragraph.createRun().getCTR().addNewDrawing()
.addNewInline();
String picXml = ""
+ "<a:graphic xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\">"
+ " <a:graphicData uri=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
+ " <pic:pic xmlns:pic=\"http://schemas.openxmlformats.org/drawingml/2006/picture\">"
+ " <pic:nvPicPr>" + " <pic:cNvPr id=\""
+ id
+ "\" name=\"Generated\"/>"
+ " <pic:cNvPicPr/>"
+ " </pic:nvPicPr>"
+ " <pic:blipFill>"
+ " <a:blip r:embed=\""
+ blipId
+ "\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"/>"
+ " <a:stretch>"
+ " <a:fillRect/>"
+ " </a:stretch>"
+ " </pic:blipFill>"
+ " <pic:spPr>"
+ " <a:xfrm>"
+ " <a:off x=\"0\" y=\"0\"/>"
+ " <a:ext cx=\""
+ width
+ "\" cy=\""
+ height
+ "\"/>"
+ " </a:xfrm>"
+ " <a:prstGeom prst=\"rect\">"
+ " <a:avLst/>"
+ " </a:prstGeom>"
+ " </pic:spPr>"
+ " </pic:pic>"
+ " </a:graphicData>" + "</a:graphic>";
inline.addNewGraphic().addNewGraphicData();
XmlToken xmlToken = null;
try {
xmlToken = XmlToken.Factory.parse(picXml);
} catch (XmlException xe) {
xe.printStackTrace();
}
inline.set(xmlToken);
inline.setDistT(0);
inline.setDistB(0);
inline.setDistL(0);
inline.setDistR(0);
CTPositiveSize2D extent = inline.addNewExtent();
extent.setCx(width);
extent.setCy(height);
CTNonVisualDrawingProps docPr = inline.addNewDocPr();
docPr.setId(id);
docPr.setName("图片名称");
docPr.setDescr("描述信息");
}
}
对应封装方法 ----------表格操作
private void insertTable(CustomXWPFDocument document) {
//创建表格接受参数-->外层list是行内层是列
List<List<String>> tableList = new ArrayList();
List<String> cells = new ArrayList<>();
cells.add("1");
cells.add("张三");
cells.add("100");
tableList.add(cells);
List<String> cellList = new ArrayList<>();
cellList.add("2");
cellList.add("李四");
cellList.add("10");
tableList.add(cellList);
//获取表格位置
List<XWPFTable> tables = document.getTables();
WordUtil.insertTable(tables.get(0),tableList);
}
表格操作用到的工具类
/**
* 为表格插入数据,行数不够添加新行
*
* @param table 需要插入数据的表格
* @param tableList 插入数据集合
*/
public static void insertTable(XWPFTable table, List<List<String>> tableList) {
//创建行,根据需要插入的数据添加新行,不处理表头
for (int i = 1; i <= tableList.size(); i++) {
table.createRow();
}
//遍历表格插入数据
List<XWPFTableRow> rows = table.getRows();
for (int i = 1; i < rows.size(); i++) {
XWPFTableRow newRow = table.getRow(i);
List<XWPFTableCell> cells = newRow.getTableCells();
for (int j = 0; j < cells.size(); j++) {
XWPFTableCell cell = cells.get(j);
cell.setText(tableList.get(i - 1).get(j));
//表格样式一致-->没有此段表格会默认左对齐
//有此段会使表格格式一致
CTTc cttc = cell.getCTTc();
CTTcPr ctPr = cttc.addNewTcPr();
ctPr.addNewVAlign().setVal(STVerticalJc.CENTER);
cttc.getPList().get(0).addNewPPr().addNewJc().setVal(STJc.CENTER);
}
}
}
对应封装方法 ----------段落内容替换操作
private void insertText(CustomXWPFDocument document) {
//声明替换模板对象
Map textMap = new HashMap();
textMap.put("${maxName}","张三");
textMap.put("${maxScore}", "100");
textMap.put("${minName}","李四");
textMap.put("${minScore}", "10");
//替换模板数据
WordUtil.changeText(document,textMap);
}
段落内容操作用到的工具类
/**
* 替换段落文本
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void changeText(XWPFDocument document, Map<String, Object> textMap) {
//获取段落集合
List<XWPFParagraph> paragraphs = document.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
//获取到段落中的所有文本内容
String text = paragraph.getText();
//判断此段落中是否有需要进行替换的文本
if (checkText(text)) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
//替换模板原来位置
run.setText(changeValue(run.toString(), textMap), 0);
}
}
}
}
/**
* 判断文本中是否包含$
*
* @param text 文本
* @return 包含返回true, 不包含返回false
*/
public static boolean checkText(String text) {
boolean check = false;
if (text.indexOf("$") != -1) {
check = true;
}
return check;
}
/**
* 替换模板${}
*/
private static Object changeValue(String value, Map<String, Object> textMap) {
Set<Map.Entry<String, Object>> textSets = textMap.entrySet();
Object valu = "";
for (Map.Entry<String, Object> textSet : textSets) {
// 匹配模板与替换值 格式${key}
String key = textSet.getKey();
if (value.contains(key)) {
valu = textSet.getValue();
}
}
return valu;
}
效果图
如果想设置单独的样式
比如加颜色、居中等,要确保.xml中替换单位为一行
效果