Apache POI 简介是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office(Excel、WORD、PowerPoint、Visio等)格式档案读和写的功能。
官网: http://poi.apache.org/index.html
API帮助文档: http://poi.apache.org/apidocs/index.html
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.0.0</version>
</dependency>
HSSF是POI项目对Excel '97(.xls)文件格式的纯Java实现。XSSF是POI项目对Excel 2007 OOXML(.xlsx)文 件格式的纯Java实现。
HSSF和XSSF提供了读取电子表格的方法,以创建,修改,读取和写入XLS电子表格。
Workbook wb = new HSSFWorkbook();
...
try (OutputStream fileOut = new FileOutputStream("workbook.xls")) {
wb.write(fileOut);
}
或者
Workbook wb = new XSSFWorkbook();
...
try (OutputStream fileOut = new FileOutputStream("workbook.xlsx")) {
wb.write(fileOut);
}
//创建默认名称的Sheet
Sheet sheet = wb.createSheet();
//创建指定名称的Sheet
Sheet my_sheet = wb.createSheet("my_sheet");
// 创建行
Row row = sheet.createRow(0);
// 创建单元格
Cell cell = row.createCell(0);
//设置值
cell.setCellValue(1);
或者链式编程
sheet.createRow(0).createCell(1).setCellValue(88);
sheet.createRow(0).createCell(2).setCellValue(true);
CellStyle cellStyle = wb.createCellStyle();
CreationHelper creationHelper = wb.getCreationHelper();
cellStyle.setDataFormat(creationHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
cell = row.createCell(3);
cell.setCellValue(new Date());
cell.setCellStyle(cellStyle);
//你也能使用日期工具类 java.util.Calendar
cell = row.createCell(4);
cell.setCellValue(Calendar.getInstance());
cell.setCellStyle(cellStyle);
当打开工作簿(.xls HSSFWorkbook或.xlsx XSSFWorkbook)时,可以从File 或InputStream加载工作簿。使用File对象可以减少内存消耗,而InputStream需要更多内存,因为它必须缓冲整个文件。
如果使用WorkbookFactory,则使用其中一个非常容易:
// Use a file
Workbook wb = WorkbookFactory.create(new File("MyExcel.xls"));
// Use an InputStream, needs more memory
Workbook wb = WorkbookFactory.create(new FileInputStream("MyExcel.xlsx"));
public static void main(String[] args) throws Exception {
Workbook wb = new XSSFWorkbook(); //or new HSSFWorkbook();
Sheet sheet = wb.createSheet();
Row row = sheet.createRow(2);
row.setHeightInPoints(30);
createCell(wb, row, 0, HorizontalAlignment.CENTER, VerticalAlignment.BOTTOM);
createCell(wb, row, 1, HorizontalAlignment.CENTER_SELECTION, VerticalAlignment.BOTTOM);
createCell(wb, row, 2, HorizontalAlignment.FILL, VerticalAlignment.CENTER);
createCell(wb, row, 3, HorizontalAlignment.GENERAL, VerticalAlignment.CENTER);
createCell(wb, row, 4, HorizontalAlignment.JUSTIFY, VerticalAlignment.JUSTIFY);
createCell(wb, row, 5, HorizontalAlignment.LEFT, VerticalAlignment.TOP);
createCell(wb, row, 6, HorizontalAlignment.RIGHT, VerticalAlignment.TOP);
// Write the output to a file
try (OutputStream fileOut = new FileOutputStream("writeExcel.xlsx")) {
wb.write(fileOut);
}
wb.close();
}
private static void createCell(Workbook wb, Row row, int column, HorizontalAlignment halign, VerticalAlignment valign) {
Cell cell = row.createCell(column);
cell.setCellValue("Align It");
CellStyle cellStyle = wb.createCellStyle();
cellStyle.setAlignment(halign);
cellStyle.setVerticalAlignment(valign);
cell.setCellStyle(cellStyle);
}
设置边框的样式与颜色
CellStyle style = wb.createCellStyle();
style.setBorderBottom(BorderStyle.THIN);
style.setBottomBorderColor(IndexedColors.BLACK.getIndex());
style.setBorderLeft(BorderStyle.THIN);
style.setLeftBorderColor(IndexedColors.GREEN.getIndex());
style.setBorderRight(BorderStyle.THIN);
style.setRightBorderColor(IndexedColors.BLUE.getIndex());
style.setBorderTop(BorderStyle.MEDIUM_DASHED);
style.setTopBorderColor(IndexedColors.BLACK.getIndex());
cell.setCellStyle(style);
要获取单元格的内容,您首先需要知道它是哪种单元格.
@Test
public void cellValue() throws IOException {
HSSFWorkbook wb = new HSSFWorkbook(new FileInputStream("cellValue.xls"));
DataFormatter formatter = new DataFormatter();
Sheet sheet1 = wb.getSheetAt(0);
for (Row row : sheet1) {
for (Cell cell : row) {
CellReference cellRef = new CellReference(row.getRowNum(), cell.getColumnIndex());
System.out.print(cellRef.formatAsString());
System.out.print(" - ");
//能够获取任何单元格类型的值
String text = formatter.formatCellValue(cell);
System.out.println(text);
//或者
// 判断单元格类型
switch (cell.getCellType()) {
case STRING:
System.out.println(cell.getRichStringCellValue().getString());
break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String value = sdf.format(cell.getDateCellValue());
System.out.println(value);
} else {
System.out.println(cell.getNumericCellValue());
}
break;
case BOOLEAN:
System.out.println(cell.getBooleanCellValue());
break;
case FORMULA:
System.out.println(cell.getCellFormula());
break;
case BLANK:
System.out.println("");
break;
case ERROR:
System.out.println("未知错误");
default:
System.out.println("未知类型");
}
}
}
}
对于大多数文本提取要求,标准的ExcelExtractor类应提供您所需要的全部。
@Test
public void textExtraction() throws IOException {
InputStream inp = new FileInputStream("cellValue.xls");
HSSFWorkbook wb = new HSSFWorkbook(new POIFSFileSystem(inp));
ExcelExtractor extractor = new ExcelExtractor(wb);
extractor.setFormulasNotResults(true);
extractor.setIncludeSheetNames(false);
String text = extractor.getText();
System.out.println(text);
wb.close();
}
CellStyle style = wb.createCellStyle();
style.setFillBackgroundColor(IndexedColors.AQUA.getIndex());//填充背景颜色
style.setFillPattern(FillPatternType.BIG_SPOTS); //填充模式
Cell cell = row.createCell(1);
cell.setCellStyle(style);
style.setFillForegroundColor(IndexedColors.ORANGE.getIndex());//填充前景色
style.setFillPattern(FillPatternType.SOLID_FOREGROUND); //填充模式
//参数1: first row (0-based) 开始行
//参数2: last row (0-based) 结束行
//参数3: first column (0-based) 开始列
//参数4: last column (0-based) 结束列
sheet.addMergedRegion(new CellRangeAddress(1, 1, 1, 2));
Font font = wb.createFont();
font.setFontHeightInPoints((short)24);
font.setFontName("Courier New");
font.setItalic(true); //斜体
font.setStrikeout(true); //删除线
font.setBold(true);//加粗
CellStyle style = wb.createCellStyle();
style.setFont(font);
cell.setCellStyle(style);
检查调用createRow() 是否在外层循环调用的,如果是在内层循环调用就会出现这个问题,因为程序会不停的重新创建行,直至最后一个cell中的数据写入,跳至下一行同上,所以会出现只有最后一列数据的情况。
原因: 单元格的宽度不够
拉开单元格宽度发现正常显示了
Apache poi的hwpf模块是专门用来对word doc文件进行读写操作的。在hwpf里面我们使用HWPFDocument来表示一个word doc文档。在HWPFDocument里面有这么几个概念:
Range:它表示一个范围,这个范围可以是整个文档,也可以是里面的某一小节(Section),也可以是某一个段落(Paragraph),还可以是拥有共同属性的一段文本(CharacterRun)。
Section:word文档的一个小节,一个word文档可以由多个小节构成。
Paragraph:word文档的一个段落,一个小节可以由多个段落构成。
CharacterRun:具有相同属性的一段文本,一个段落可以由多个CharacterRun组成。
Table:一个表格。
TableRow:表格对应的行。
TableCell:表格对应的单元格。
Section、Paragraph、CharacterRun和Table都继承自Range。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.0.0</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.0.0</version>
</dependency>
在日常应用中,我们从word文件里面读取信息的情况非常少见,更多的还是把内容写入到word文件中。使用POI从word doc文件读取数据时主要有两种方式:通过WordExtractor读和通过HWPFDocument读。在WordExtractor内部进行信息读取时还是通过HWPFDocument来获取的。
在使用WordExtractor读文件时我们只能读到文件的文本内容和基于文档的一些属性,至于文档内容的属性等是无法读到的。如果要读到文档内容的属性则需要使用HWPFDocument来读取了。下面是使用WordExtractor读取文件的一个示例:
public class HwpfTest {
@Test
public void testReadByExtractor() throws IOException {
InputStream is = new FileInputStream("test.doc");
WordExtractor extractor = new WordExtractor(is);
//输出word文档所有的文本
System.out.println(extractor.getText());
System.out.println("=======================");
System.out.println(extractor.getTextFromPieces());
//输出页眉的内容
System.out.println("页眉: " + extractor.getHeaderText());
//输出页脚的内容
System.out.println("页脚: " + extractor.getFooterText());
//输出当前word文档的元数据信息,包括作者、文档的修改时间等。
System.out.println(extractor.getMetadataTextExtractor().getText());
//获取各个段落的文本
String paraTexts[] = extractor.getParagraphText();
for (int i = 0; i < paraTexts.length; i++) {
System.out.println("Paragraph " + (i + 1) + " : " + paraTexts[i]);
}
//输出当前word的一些信息
printInfo(extractor.getSummaryInformation());
//输出当前word的一些信息
this.printInfo(extractor.getDocSummaryInformation());
this.closeStream(is);
}
/**
* 输出SummaryInfomation
*
* @param info
*/
private void printInfo(SummaryInformation info) {
//作者
System.out.println(info.getAuthor());
//字符统计
System.out.println(info.getCharCount());
//页数
System.out.println(info.getPageCount());
//标题
System.out.println(info.getTitle());
//主题
System.out.println(info.getSubject());
}
/**
* 输出DocumentSummaryInfomation
*
* @param info
*/
private void printInfo(DocumentSummaryInformation info) {
//分类
System.out.println(info.getCategory());
//公司
System.out.println(info.getCompany());
}
/**
* 关闭输入流
*
* @param is
*/
private void closeStream(InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
HWPFDocument是当前Word文档的代表,它的功能比WordExtractor要强。通过它我们可以读取文档中的表格、列表等,还可以对文档的内容进行新增、修改和删除操作。只是在进行完这些新增、修改和删除后相关信息是保存在HWPFDocument中的,也就是说我们改变的是HWPFDocument,而不是磁盘上的文件。如果要使这些修改生效的话,我们可以调用HWPFDocument的write方法把修改后的HWPFDocument输出到指定的输出流中。这可以是原文件的输出流,也可以是新文件的输出流(相当于另存为)或其它输出流。
@Test
public void testReadByDoc() throws Exception {
InputStream is = new FileInputStream("test.doc");
HWPFDocument doc = new HWPFDocument(is);
//输出书签信息
this.printInfo(doc.getBookmarks());
//输出文本
System.out.println(doc.getDocumentText());
Range range = doc.getRange();
// this.insertInfo(range);
this.printInfo(range);
//读表格
this.readTable(range);
//读列表
this.readList(range);
//删除range
Range r = new Range(2, 5, doc);
r.delete();//在内存中进行删除,如果需要保存到文件中需要再把它写回文件
//把当前HWPFDocument写到输出流中
doc.write(new FileOutputStream("test.doc"));
this.closeStream(is);
}
/**
* 关闭输入流
* @param is
*/
private void closeStream(InputStream is) {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 输出书签信息
* @param bookmarks
*/
private void printInfo(Bookmarks bookmarks) {
int count = bookmarks.getBookmarksCount();
System.out.println("书签数量:" + count);
Bookmark bookmark;
for (int i=0; i<count; i++) {
bookmark = bookmarks.getBookmark(i);
System.out.println("书签" + (i+1) + "的名称是:" + bookmark.getName());
System.out.println("开始位置:" + bookmark.getStart());
System.out.println("结束位置:" + bookmark.getEnd());
}
}
/**
* 读表格
* 每一个回车符代表一个段落,所以对于表格而言,每一个单元格至少包含一个段落,每行结束都是一个段落。
* @param range
*/
private void readTable(Range range) {
System.out.println("=============readTable start================");
//遍历range范围内的table。
TableIterator tableIter = new TableIterator(range);
Table table;
TableRow row;
TableCell cell;
while (tableIter.hasNext()) {
table = tableIter.next();
int rowNum = table.numRows();
for (int j=0; j<rowNum; j++) {
row = table.getRow(j);
int cellNum = row.numCells();
for (int k=0; k<cellNum; k++) {
cell = row.getCell(k);
//输出单元格的文本
System.out.print(cell.text().trim()+" ");
}
System.out.println();
}
}
System.out.println("=============readTable end==============");
}
/**
* 读列表
* @param range
*/
private void readList(Range range) {
System.out.println("=========readList start==========");
int num = range.numParagraphs();
Paragraph para;
for (int i=0; i<num; i++) {
para = range.getParagraph(i);
if (para.isInList()) {
System.out.println("list: " + para.text());
}
}
System.out.println("=========readList end==========");
}
/**
* 输出Range
* @param range
*/
private void printInfo(Range range) {
//获取段落数
int paraNum = range.numParagraphs();
System.out.println(paraNum);
for (int i=0; i<paraNum; i++) {
System.out.println("段落" + (i+1) + ":" + range.getParagraph(i).text());
if (i == (paraNum-1)) {
this.insertInfo(range.getParagraph(i));
}
}
int secNum = range.numSections();
System.out.println(secNum);
Section section;
for (int i=0; i<secNum; i++) {
section = range.getSection(i);
System.out.println(section.getMarginLeft());
System.out.println(section.getMarginRight());
System.out.println(section.getMarginTop());
System.out.println(section.getMarginBottom());
System.out.println(section.getPageHeight());
System.out.println(section.text());
}
}
/**
* 插入内容到Range,这里只会写到内存中
* @param range
*/
private void insertInfo(Range range) {
range.insertAfter("Hello");
}
在实际应用中,我们在生成word文件的时候都是生成某一类文件,该类文件的格式是固定的,只是某些字段不一样罢了。所以在实际应用中,我们大可不必将整个word文件的内容都通过HWPFDocument生成。而是先在磁盘上新建一个word文档,其内容就是我们需要生成的word文件的内容,然后把里面一些属于变量的内容使用类似于“${paramName}”这样的方式代替。这样我们在基于某些信息生成word文件的时候,只需要获取基于该word文件的HWPFDocument,然后调用Range的replaceText()方法把对应的变量替换为对应的值即可,之后再把当前的HWPFDocument写入到新的输出流中。这种方式在实际应用中用的比较多,因为它不但可以减少我们的工作量,还可以让文本的格式更加的清晰。下面我们就来基于这种方式做一个示例。
假设我们现在拥有一些变动的信息,然后需要通过这些信息生成如下格式的word doc文件:model.doc
姓名 | 年龄 | 生日 |
---|---|---|
${name} | ${age} | ${birthday} |
替换模版中的变量,生成新的doc文件
@Test
public void testWrite() throws Exception {
String templatePath = "model.doc";
InputStream is = new FileInputStream(templatePath);
HWPFDocument doc = new HWPFDocument(is);
Range range = doc.getRange();
range.replaceText("${title}", "体检");
range.replaceText("${name}", "张三");
range.replaceText("${age}", "22");
range.replaceText("${birthday}", new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
OutputStream os = new FileOutputStream("new.doc");
//把doc输出到输出流中
doc.write(os);
os.close();
is.close();
}
API文档:https://poi.apache.org/apidocs/4.0/index.html
这是 org.apache.poi.xwpf.usermodel 包下的类。 它用于创建.docx文件格式的MS-Word文档。
类方法:
No. | 方法和说明 |
---|---|
1 | commit()提交并保存文档。 |
2 | createParagraph()在本文档中追加一个新段落。 |
3 | createTable()默认情况下,创建一个包含一行和一列的空表。 |
4 | createTOC()创建Word文档的内容表。 |
5 | getParagraphs()返回包含页眉或页脚文本的段落。 |
6 | getStyle()返回使用的样式对象。 |
这是 org.apache.poi.xwpf.usermodel 包下的类,用于在Word文档中创建段落。 此实例也用于将所有类型的元素添加到Word文档中。
类方法:
No. | 方法和说明 |
---|---|
1 | createRun()将新运行附加到此段落。 |
2 | getAlignment()返回将应用于本段文本的段落对齐方式。 |
3 | setAlignment(ParagraphAlignment align)指定应适用于本段文本的段落对齐方式。 |
4 | setBorderBottom(Borders border)指定应显示在一组段落下方的边框,这些段落具有相同的一组段落边框设置。 |
5 | setBorderLeft(Borders border)指定应在页面左侧围绕指定段落显示的边框。 |
6 | setBorderRight(Borders border)指定应在页面右侧围绕指定段落显示的边框。 |
7 | setBorderTop(Borders border)指定应显示在具有相同的一组段落边框设置的一组段落上方的边框。 |
这是 org.apache.poi.xwpf.usermodel 包下的类,用于向段落中添加文本区域。
类方法:
No. | 方法和说明 |
---|---|
1 | addBreak()指定断点应放置在运行内容的当前位置。 |
2 | addTab()指定制表符应放置在运行内容中的当前位置。 |
3 | setColor(java.lang.String rgbStr)设置文本颜色。 |
4 | setFontSize(int size)指定在显示时应用于此运行内容中所有非复杂脚本字符的字体大小。 |
5 | setText(java.lang.String value)设置此文本运行的文本。 |
6 | setBold(boolean value)指定在文档中显示时,粗体属性是否应用于此运行内容中的所有非复杂脚本字符。 |
这是 org.apache.poi.xwpf.usermodel 包下的类,用于向word文档中的对象元素添加不同的样式。
类方法:
No. | 方法和说明 |
---|---|
1 | getNextStyleID()它用于获取下一个样式的StyleID。 |
2 | getStyleId()它用于获取样式的StyleID。 |
3 | getStyles()它用于获取样式。 |
4 | setStyleId(java.lang.String styleId)它用于设置styleID。 |
这是 org.apache.poi.xwpf.usermodel 包下的类,用于将表数据添加到Word文档中。
类方法:
No. | 方法和说明 |
---|---|
1 | addNewCol()为此表中的每一行添加一个新列。 |
2 | addRow(XWPFTableRow row)向表中添加新行 |
3 | addRow(XWPFTableRow row, int pos)向表中指定位置添加新行 |
4 | createRow()创建一个新的XWPFTableRow对象,其具有与那一刻定义的列数一样多的单元格。 |
5 | setWidth(int width)设置列的宽度。 |
这是 org.apache.poi.xwpf.extractor 包下的类。 它是一个基本的解析器类,用于从Word文档中提取简单文本。
类方法:
No. | 方法和说明 |
---|---|
1 | getText()检索文档中的所有文本。 |
@Test
public void createDocument() throws IOException {
XWPFDocument document= new XWPFDocument();
FileOutputStream out = new FileOutputStream(new File("createdocument.docx"));
document.write(out);
out.close();
}
首先创建一个文档,然后我们可以创建一个段落。
XWPFDocument document = new XWPFDocument();
XWPFParagraph paragraph = document.createParagraph();
您可以使用运行输入文本或任何对象元素。 使用Paragraph实例,您可以创建运行。
XWPFRun run = paragraph.createRun();
run.setText("Hello World");
完整代码
@Test
public void createParagraph() throws IOException {
XWPFDocument document = new XWPFDocument();
FileOutputStream out = new FileOutputStream(new File("createparagraph.docx"));
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.setText("Hello World");
document.write(out);
out.close();
}
以下代码用于在文档中应用边框:
@Test
public void applyingBorder() throws IOException {
XWPFDocument document= new XWPFDocument();
FileOutputStream out = new FileOutputStream(new File("applyingborder.docx"));
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run=paragraph.createRun();
run.setText("您将学习如何使用Java编程将边框应用到段落。");
paragraph.setBorderBottom(Borders.SINGLE);
paragraph.setBorderLeft(Borders.SINGLE);
paragraph.setBorderRight(Borders.SINGLE);
paragraph.setBorderTop(Borders.SINGLE);
document.write(out);
out.close();
}
@Test
public void createTable() throws IOException {
XWPFDocument document= new XWPFDocument();
FileOutputStream out = new FileOutputStream(new File("create_table.docx"));
//create table
XWPFTable table = document.createTable();
//create first row
XWPFTableRow tableRowOne = table.getRow(0);
tableRowOne.getCell(0).setText("col one, row one");
tableRowOne.addNewTableCell().setText("col two, row one");
tableRowOne.addNewTableCell().setText("col three, row one");
//create second row
XWPFTableRow tableRowTwo = table.createRow();
tableRowTwo.getCell(0).setText("col one, row two");
tableRowTwo.getCell(1).setText("col two, row two");
tableRowTwo.getCell(2).setText("col three, row two");
//create third row
XWPFTableRow tableRowThree = table.createRow();
tableRowThree.getCell(0).setText("col one, row three");
tableRowThree.getCell(1).setText("col two, row three");
tableRowThree.getCell(2).setText("col three, row three");
document.write(out);
out.close();
}
通常,字体样式包含:字体大小,类型,粗体,斜体和下划线。 对齐分为左,中,右,对齐。
@Test
public void fontStyle() throws IOException {
XWPFDocument document= new XWPFDocument();
//Write the Document in file system
FileOutputStream out = new FileOutputStream(new File("fontstyle.docx"));
//create paragraph
XWPFParagraph paragraph = document.createParagraph();
//Set Bold an Italic
XWPFRun paragraphOneRunOne = paragraph.createRun();
paragraphOneRunOne.setBold(true); //加粗
paragraphOneRunOne.setItalic(true);//斜体
paragraphOneRunOne.setText("Font Style");
paragraphOneRunOne.addBreak();
//Set text Position
XWPFRun paragraphOneRunTwo = paragraph.createRun();
paragraphOneRunTwo.setUnderline(UnderlinePatterns.SINGLE); //下划线
paragraphOneRunTwo.setText("Font Style two");
paragraphOneRunTwo.setTextPosition(30);
//Set Strike through and Font Size and Subscript
XWPFRun paragraphOneRunThree = paragraph.createRun();
paragraphOneRunThree.setStrike(true);
paragraphOneRunThree.setFontSize(18);
paragraphOneRunThree.setSubscript(VerticalAlign.SUBSCRIPT);
paragraphOneRunThree.setText(" Different Font Styles");
document.write(out);
out.close();
}
以下代码用于设置与段落文本的对齐方式:
@Test
public void alignParagraph() throws IOException {
XWPFDocument document= new XWPFDocument();
FileOutputStream out = new FileOutputStream(new File("alignparagraph.docx"));
//create paragraph
XWPFParagraph paragraph = document.createParagraph();
//Set alignment paragraph to RIGHT
paragraph.setAlignment(ParagraphAlignment.RIGHT);
XWPFRun run=paragraph.createRun();
run.setText("At tutorialspoint.com, we strive hard to " +
"provide quality tutorials for self-learning " +
"purpose in the domains of Academics, Information " +
"Technology, Management and Computer Programming " +
"Languages.");
//Create Another paragraph
paragraph=document.createParagraph();
//Set alignment paragraph to CENTER
paragraph.setAlignment(ParagraphAlignment.CENTER);
run=paragraph.createRun();
run.setText("The endeavour started by Mohtashim, an AMU " +
"alumni, who is the founder and the managing director " +
"of Tutorials Point (I) Pvt. Ltd. He came up with the " +
"website tutorialspoint.com in year 2006 with the help" +
"of handpicked freelancers, with an array of tutorials" +
" for computer programming languages. ");
document.write(out);
out.close();
}
要具体操作通过XWPFDocument 可以获得的docx中的各种对象,我们离不开一个对象为XWPFRun对象,API结构org.apache.poi.xwpf.usermodel.XWPFRun。其描述为:XWPFRun object defines a region of text with a common set of properties。通过描述我们不难理解其作用为设置文本对象的各种属性。
通过XWPFDocument 获取对象
//解析docx模板并获取document对象
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
//获取整个文本对象
List<XWPFParagraph> allParagraph = document.getParagraphs();
//获取整个表格对象
List<XWPFTable> allTable = document.getTables();
//获取图片对象
XWPFPictureData pic = document.getPictureDataByID("PICId");
首先建一个很简单的word模板model.docx,我们通过操作对象获取word中的文本内容
姓名: ${name}
性别: ${sex}
需要替换的表格
家庭地址 | 电话 | 邮箱 |
---|---|---|
${address} | ${phone} | ${email} |
需要插入的表格
电话 | 微信 | 微博 | |
---|---|---|---|
package com.newland.poi.word;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.xwpf.usermodel.*;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
/**
* @author 张宝奎
* @date 2021/3/16
*/
public class WordTemplateUtil {
/**
* 根据模板生成新word文档
* 判断表格是需要替换还是需要插入,判断逻辑有$为替换,表格无$为插入
*
* @param inputUrl 模板存放地址
* @param outputUrl 新文档存放地址
* @param textMap 需要替换的信息集合
* @param tableList 需要插入的表格信息集合
* @return 成功返回true, 失败返回false
*/
public static boolean changWord(String inputUrl, String outputUrl, Map<String, String> textMap, List<String[]> tableList) {
//模板转换默认成功
boolean changeFlag = true;
try {
//获取docx解析对象
XWPFDocument document = new XWPFDocument(POIXMLDocument.openPackage(inputUrl));
//解析替换文本段落对象
WordTemplateUtil.changeText(document, textMap);
//解析替换表格对象
WordTemplateUtil.changeTable(document, textMap, tableList);
//生成新的word
File file = new File(outputUrl);
FileOutputStream stream = new FileOutputStream(file);
document.write(stream);
stream.close();
} catch (IOException e) {
e.printStackTrace();
changeFlag = false;
}
return changeFlag;
}
/**
* 替换段落文本
*
* @param document docx解析对象
* @param textMap 需要替换的信息集合
*/
public static void changeText(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 textMap 需要替换的信息集合
* @param tableList 需要插入的表格信息集合
*/
public static void changeTable(XWPFDocument document, Map<String, String> textMap,
List<String[]> tableList) {
//获取表格对象集合
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);
} else {
// System.out.println("插入"+table.getText());
insertTable(table, tableList);
}
}
}
}
/**
* 遍历表格
*
* @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 table 需要插入数据的表格
* @param tableList 插入数据集合
*/
public static void insertTable(XWPFTable table, List<String[]> tableList) {
//创建行,根据需要插入的数据添加新行,不处理表头
for (int i = 1; i < tableList.size(); i++) {
XWPFTableRow row = 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)[j]);
}
}
}
/**
* 判断文本中时候包含$
*
* @param text 文本
* @return 包含返回true, 不包含返回false
*/
public static boolean checkText(String text) {
boolean check = false;
if (text.indexOf("$") != -1) {
check = true;
}
return check;
}
/**
* 匹配传入信息集合与模板
*
* @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;
}
public static void main(String[] args) {
//模板文件地址
String inputUrl = "model.docx";
//新生产的模板文件
String outputUrl = "new.docx";
Map<String, String> testMap = new HashMap<String, String>();
testMap.put("name", "小明");
testMap.put("sex", "男");
testMap.put("address", "软件园");
testMap.put("phone", "88888888");
List<String[]> testList = new ArrayList<String[]>();
testList.add(new String[]{"1", "1AA", "1BB", "1CC"});
testList.add(new String[]{"2", "2AA", "2BB", "2CC"});
testList.add(new String[]{"3", "3AA", "3BB", "3CC"});
testList.add(new String[]{"4", "4AA", "4BB", "4CC"});
WordTemplateUtil.changWord(inputUrl, outputUrl, testMap, testList);
}
}