Java POI导出word文件及生成表格
HWPF是处理 Microsoft Word 97(-2007) .doc文件格式,它还为较旧的Word 6和Word 95文件格式提供了有限的只读支持。包含在poi-scratchpad-XXX.jar中。XWPF是处理 Word 2007 .docx文件格式,包含在poi-ooxml-XXX.jar中。虽然HWPF和XWPF提供类似的功能,但目前两者...
HWPF是处理 Microsoft Word 97(-2007) .doc文件格式,它还为较旧的Word 6和Word 95文件格式提供了有限的只读支持。包含在poi-scratchpad-XXX.jar中。
XWPF是处理 Word 2007 .docx文件格式,包含在poi-ooxml-XXX.jar中。
虽然HWPF和XWPF提供类似的功能,但目前两者之间没有公共接口。
这里POI使用 4.1.2版本,处理 .docx文件格式,word文件中只包含普通数据和表格数据。在Java POI通用导入导出Excel 的项目基础上简单实现的
一、新建word模板文件(.docx)
1、新建word模板文件,并在word文件中设置模板标签(这里自定义使用 ${变量名})。
普通数据:一般标签以 ${变量名} 的格式命名,其中 $ 和 {} 一定是英文符号,如 ${project} , ${time}。注意标签名不要重复
表格数据:由于表格数据是由多组相同格式的数据组成,所以无需设置标签,只需要在word文件中新建空白表格,并设置表头即可,系统可以根据相应单元格的位置对表格数据进行设置(这就是表格的操作了)。
word模板文件定义好放在项目的资源路径下。在代码中使用资源路径来获取对应的模板文件。
注意:如果遇到poi读取如${name}不能识别为一个整体时,可以使用
1)使用word的域操作。
2)在text文档中写好,如${name},然后再复制到word中,不要在word中一个一个敲,不然可能不会被poi识别为一个整体。
2、常用类说明
XWPFDocument:用于处理.docx文件。
XWPFParagraph:保存文本的段落 。在文档、表格、标题、页眉或页脚等都可存在段落。一个段落有很多样式化信息,但是实际的文本(可能还有更多的样式化)保存在子XWPFRuns上。
XWPFRun:XWPFRun对象用一组公共属性定义一个文本区域。对文本的内容,字体,颜色,大小,样式等操作。
XWPFTable:指定文档中出现的表的内容。表是一组按行和列排列的段落(和其他块级内容)。
XWPFTableRow:表示XWPFTable中的一行。行大多只有大小和样式,更多的内容存在于子XWPFTableCells中。
XWPFTableCell:表示XWPFTable中的单元格。单元格是存放实际内容(段落等)的东西。
具体的说明和操作方法查看官方API:https://poi.apache.org/apidocs/index.html
1)获取文件中文档的文本内容
public static void main(String[] args) throws Exception {
StringBuffer tableText = new StringBuffer();
String templatePath = PoiSerivce.class.getResource("/static/template/person.docx").getPath();
//解析docx模板并获取document对象
XWPFDocument document = new XWPFDocument(new FileInputStream(templatePath));
//获取整个文本对象
List<XWPFParagraph> paragraphs = document.getParagraphs();
//获取XWPFRun对象输出整个文档中的文本内容
paragraphs.forEach(xwpfParagraph -> {
List<XWPFRun> runs = xwpfParagraph.getRuns();
for (XWPFRun run : runs) {
tableText.append(run.toString());
}
});
System.out.println(tableText.toString());
}
2)获取文件中表格的文本内容
public static void main(String[] args) throws Exception {
StringBuffer tableText = new StringBuffer();
String templatePath = PoiSerivce.class.getResource("/static/template/person.docx").getPath();
//解析docx模板并获取document对象
XWPFDocument document = new XWPFDocument(new FileInputStream(templatePath));
//获取全部表格对象
List<XWPFTable> tables = document.getTables();
//获取表格中的文本内容
tables.forEach(xwpfTable -> {
//获取表格行数据
List<XWPFTableRow> rows = xwpfTable.getRows();
for (XWPFTableRow row : rows) {
//获取表格单元格数据
List<XWPFTableCell> tableCells = row.getTableCells();
for (XWPFTableCell tableCell : tableCells) {
List<XWPFParagraph> paragraphs = tableCell.getParagraphs();
paragraphs.forEach(xwpfParagraph -> {
List<XWPFRun> runs = xwpfParagraph.getRuns();
for (XWPFRun run : runs) {
tableText.append(run.toString());
}
});
}
}
});
System.out.println(tableText.toString());
}
二、导出.docx文件:使用XWPFDocument
1、controller
@RequestMapping(value="/exportWord")
public ResponseEntity<byte[]> exportWord() throws Exception{
String fileName = "导出docx数据.docx";
HttpHeaders headers = new HttpHeaders();
//下载显示的文件名,并解决中文名称乱码问题
String downloadFileName = new String(fileName.getBytes(StandardCharsets.UTF_8.name()),StandardCharsets.ISO_8859_1.name());
//通知浏览器以attachment(下载方式)打开
headers.setContentDispositionFormData("attachment", downloadFileName);
//applicatin/octet-stream: 二进制流数据(最常见的文件下载)
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
byte[] byteArr = poiSerivce.exportWord();
return new ResponseEntity<byte[]>(byteArr, headers, HttpStatus.CREATED);
}
2、service
public byte[] exportWord() {
// 模拟点数据
Map<String, String> paragraphMap = new HashMap<>();
Map<String, String> tableMap = new HashMap<>();
List<String[]> familyList = new ArrayList<>();
paragraphMap.put("number", "10000");
paragraphMap.put("date", "2020-03-25");
tableMap.put("name", "赵云");
tableMap.put("sexual", "男");
tableMap.put("birthday", "2020-01-01");
tableMap.put("identify", "123456789");
tableMap.put("phone", "18377776666");
tableMap.put("address", "王者荣耀");
tableMap.put("domicile", "中国-腾讯");
tableMap.put("QQ", "是");
tableMap.put("chat", "是");
tableMap.put("blog", "是");
familyList.add(new String[]{"露娜", "女", "野友", "666", "6660"});
familyList.add(new String[]{"鲁班", "男", "射友", "222", "2220"});
familyList.add(new String[]{"程咬金", "男", "肉友", "999", "9990"});
familyList.add(new String[]{"太乙真人", "男", "辅友", "111", "1110"});
familyList.add(new String[]{"貂蝉", "女", "法友", "888", "8880"});
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
String templatePath = this.getClass().getClassLoader().getResource("static/template/person.docx").getPath();
XWPFDocument document = null;
try {
//解析docx模板并获取document对象
document = new XWPFDocument(new FileInputStream(templatePath));
ExportWordUtil.changeParagraphText(document,paragraphMap);
ExportWordUtil.changeTableText(document, tableMap);
ExportWordUtil.copyHeaderInsertText(document, familyList , 7);
document.write(byteOut);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (document != null) {
try {
document.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return byteOut.toByteArray();
}
3、工具类
import org.apache.poi.xwpf.usermodel.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @Description:
* @Auther: leijq
* @Date: 2020-03-25 14:59
* @Version: V1.0
*/
public class ExportWordUtil {
private ExportWordUtil() {
}
/**
* 替换文档中段落文本
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void changeParagraphText(XWPFDocument document, Map<String, String> 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 document docx解析对象
* @param tableList 需要插入数据集合
* @param headerIndex 表头的行索引,从0开始
*/
public static void copyHeaderInsertText(XWPFDocument document, List<String[]> tableList, int headerIndex){
if(null == tableList){
return;
}
//获取表格对象集合
List<XWPFTable> tables = document.getTables();
for (XWPFTable table : tables) {
XWPFTableRow copyRow = table.getRow(headerIndex);
List<XWPFTableCell> cellList = copyRow.getTableCells();
if (null == cellList) {
break;
}
//遍历要添加的数据的list
for (int i = 0; i < tableList.size(); i++) {
//插入一行
XWPFTableRow targetRow = table.insertNewTableRow(headerIndex + 1 + i);
//复制行属性
targetRow.getCtRow().setTrPr(copyRow.getCtRow().getTrPr());
String[] strings = tableList.get(i);
for (int j = 0; j < strings.length; j++) {
XWPFTableCell sourceCell = cellList.get(j);
//插入一个单元格
XWPFTableCell targetCell = targetRow.addNewTableCell();
//复制列属性
targetCell.getCTTc().setTcPr(sourceCell.getCTTc().getTcPr());
targetCell.setText(strings[j]);
}
}
}
}
/**
* 替换表格对象方法
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void changeTableText(XWPFDocument document, Map<String, String> textMap) {
//获取表格对象集合
List<XWPFTable> tables = document.getTables();
for (int i = 0; i < tables.size(); i++) {
//只处理行数大于等于2的表格
XWPFTable table = tables.get(i);
if (table.getRows().size() > 1) {
//判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
if (checkText(table.getText())) {
List<XWPFTableRow> rows = table.getRows();
//遍历表格,并替换模板
eachTable(rows, textMap);
}
}
}
}
/**
* 遍历表格,并替换模板
*
* @param rows 表格行对象
* @param textMap 需要替换的信息集合
*/
public static void eachTable(List<XWPFTableRow> rows, Map<String, String> textMap) {
for (XWPFTableRow row : rows) {
List<XWPFTableCell> cells = row.getTableCells();
for (XWPFTableCell cell : cells) {
//判断单元格是否需要替换
if (checkText(cell.getText())) {
List<XWPFParagraph> paragraphs = cell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
run.setText(changeValue(run.toString(), textMap), 0);
}
}
}
}
}
}
/**
* 匹配传入信息集合与模板
*
* @param value 模板需要替换的区域
* @param textMap 传入信息集合
* @return 模板需要替换区域信息集合对应值
*/
public static String changeValue(String value, Map<String, String> textMap) {
Set<Map.Entry<String, String>> textSets = textMap.entrySet();
for (Map.Entry<String, String> textSet : textSets) {
//匹配模板与替换值 格式${key}
String key = "${" + textSet.getKey() + "}";
if (value.indexOf(key) != -1) {
value = textSet.getValue();
}
}
//模板未匹配到区域替换为空
if (checkText(value)) {
value = "";
}
return value;
}
/**
* 判断文本中时候包含$
*
* @param text 文本
* @return 包含返回true, 不包含返回false
*/
public static boolean checkText(String text) {
boolean check = false;
if (text.indexOf("$") != -1) {
check = true;
}
return check;
}
}
这里简单做了工具类处理。
参考文章:
ends~
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)