在实际开发中,我们可能会遇到比较复杂的导出word的业务场景,比如添加段落,添加图片,添加表格,有时候表格还有特定的样式,比如单元格水平居中,垂直居中,有背景颜色等等,或者同一个表格在一份word文件里出现在不同的地方,这个时候我们通常不能用简答版的word去开发了。虽然步骤是一样的,但是开发难度明显加大。

1. 设置模板(一般在这样的场景中,首页一般都是定制的,用java去实现出来比较费劲,所以我建议建立两份模板文件,第一份用来保存首页,第二份用来保存word文件里导出需要用到的表格或图片。)

2. 编写代码

2.1 添加依赖
<!-- Excel = EasyPoi -->
        <dependency>
            <groupId>cn.afterturn</groupId>
            <artifactId>easypoi-base</artifactId>
            <version>4.0.0</version>
            <exclusions>
                <exclusion>
                    <artifactId>commons-lang3</artifactId>
                    <groupId>org.apache.commons</groupId>
                </exclusion>
            </exclusions>
        </dependency>
2.2 编写controller
package com.mayi1203.myproject.controller;

import java.io.InputStream;
import java.io.OutputStream;
import java.math.BigDecimal;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.Document;
import org.apache.poi.xwpf.usermodel.LineSpacingRule;
import org.apache.poi.xwpf.usermodel.ParagraphAlignment;
import org.apache.poi.xwpf.usermodel.TableRowAlign;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableCell.XWPFVertAlign;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTPPr;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTShd;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STShd;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.mayi1203.myproject.entity.Entity;

import cn.afterturn.easypoi.cache.WordCache;
import cn.afterturn.easypoi.word.WordExportUtil;

@RestController
@RequestMapping(value = "/v1/test")
public class TestController {
	private static final String TEMPLATE_FILE_NAME = "word/template.docx";
	private static final String HELPER_TEMPLATE_FILE_NAME = "word/templateHelper.docx";
	private static final String IMAGE_FILE_NAME = "img/template.png";
	public static final DecimalFormat PERCENTAGE_DECIMAL_FORMAT = new DecimalFormat("0.00%");
	
	@GetMapping("exportWord")
	public void exportWord(HttpServletResponse response) throws Exception {
		// 填充参数
		Map<String, Object> map = new HashMap<>(9);
		map.put("0", "one");
		map.put("1", "two");
		map.put("2", "three");
		map.put("3", "four");
		map.put("4", "five");
		map.put("5", "six");
		
		XWPFDocument document = WordExportUtil.exportWord07(TEMPLATE_FILE_NAME, map);
		// 添加段落
		XWPFParagraph paragraph = document.createParagraph();
		// 设置段前间距,120是6磅
		paragraph.setSpacingBefore(120);
		// 设置段后间距,120是6磅
		paragraph.setSpacingAfter(120);
		// 设置行距,固定值25磅
		paragraph.setSpacingBetween(30, LineSpacingRule.EXACT);
		// 设置水平对齐方式
//		paragraph.setAlignment(ParagraphAlignment.CENTER);
		// 设置首行缩进建议用4个空格代替,代码里的单位是毫米
//		paragraph.setBorderBottom(Borders.THICK_THIN_SMALL_GAP);
		// 设置缩进文本之前,单位是毫米。
//		paragraph.setIndentationLeft(400);
		// 设置缩进文本之后,单位是毫米。
//		paragraph.setIndentFromRight(400);
		// 设置段落底纹
		CTPPr ppr = paragraph.getCTP().getPPr();
		CTShd shd = ppr.addNewShd();
		shd.setVal(STShd.CLEAR);
		// 设置段落底纹颜色
		shd.setFill("D9D9D9");
		// 添加字体
		XWPFRun run = paragraph.createRun();
		// 设置加粗
		run.setBold(true);
		// 设置字体大小,16是三号
		run.setFontSize(16);
		// 设置字符间距
		run.setCharacterSpacing(20);
		// 设置字体
		run.setFontFamily("宋体");
		// 设置文本
		run.setText("这是测试段落-------------------,昨天是520,没有情人,只能敲代码。难受想哭!!!!!!!!");
		// 设置字体颜色
		run.setColor("FF0000");
		// 设置大写,比如my name is zhangsan,会变成MY NAME IS ZHANGSAN。
//		run.setCapitalized(true);
		// 设置双删除线
//		run.setDoubleStrikethrough(true);
		// 设置着重号
//		run.setEmphasisMark("dot");
		// 设置斜体
//		run.setItalic(true);
		// 设置小型大写字母
//		run.setSmallCaps(true);
		// 设置删除线
//		run.setStrikeThrough(true);
		// 设置下划线
//		run.setUnderline(UnderlinePatterns.DASH);
		// 设置下划线颜色
//		run.setUnderlineColor("FF0000");
		// 设置消失
//		run.setVanish(true);
//		XWPFRun secondRun = paragraph.createRun();
//		// 设置上标
//		secondRun.setSubscript(VerticalAlign.SUPERSCRIPT);
//		secondRun.setText("2");
//		XWPFRun thirdRun = paragraph.createRun();
//		// 设置下标
//		thirdRun.setSubscript(VerticalAlign.SUBSCRIPT);
//		thirdRun.setText("3");
		// 换行
//		run.addBreak();
		// 添加回车
//		run.addCarriageReturn();
		// 添加图片,有时候图片展示不全,可以提高行距来使图片展示完整。
//		InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(IMAGE_FILE_NAME);
//		int pictureType = Document.PICTURE_TYPE_PNG;
		// width = 360000 * 效果图的宽度(单位厘米)/12700
//		int width = Units.toEMU(183.685);
		// height = 360000 * 效果图的高度(单位厘米)/12700
//		int height = Units.toEMU(26.646);
//		run.addPicture(inputStream, pictureType, IMAGE_FILE_NAME, width, height);	
		// 换页
//		paragraph.setPageBreak(true);
		document.createParagraph();
		XWPFDocument helperDocument = WordCache.getXWPFDocumen(HELPER_TEMPLATE_FILE_NAME);
		XWPFTable sourceTable = helperDocument.getTableArray(1);
		XWPFTableRow sourceRow = sourceTable.getRow(0);
		XWPFTable table = document.createTable(1, sourceRow.getTableCells().size());
		this.copyFirstRow(table, sourceRow);
		this.copyTableStyle(table, sourceTable);
		List<Entity> list = new ArrayList<>(10);
		this.addData(list);
		final int size = list.size();
		for(int i = 0; i < size; i++) {
			XWPFTableRow row = table.createRow();
			// 设置行高
			row.setHeight(600);
			List<XWPFTableCell> cellList = row.getTableCells();
			Entity entity = list.get(i);
			final int cellSize = cellList.size();
			for(int j = 0; j < cellSize; j++) {
				XWPFTableCell cell = cellList.get(j);
				// 设置列宽
				cell.setWidth(String.valueOf(sourceRow.getCell(j).getWidth()));
				cell.setWidthType(sourceRow.getCell(j).getWidthType());
				// 设置垂直对齐方式
				cell.setVerticalAlignment(XWPFVertAlign.CENTER);
				XWPFParagraph cellPara = cell.getParagraphArray(0);
				// 设置水平对齐方式
				cellPara.setAlignment(ParagraphAlignment.CENTER);
				XWPFRun cellRun = cellPara.createRun();
				cellRun.setFontFamily("宋体");
				cellRun.setFontSize(11);
				// 隔行换色
				if(i % 2 != 0) {
					CTShd ctshd = cell.getCTTc().addNewTcPr().addNewShd();
					ctshd.setVal(STShd.CLEAR);
					// 设置底纹颜色
					ctshd.setFill("D9D9D9");						
				}
				if(j == 0) {
					cellRun.setBold(true);
					cellRun.setText(entity.getName());
				}else if(j == 1) {
					cellRun.setText(entity.getPrice().toString());
				}else if(j == 2) {
					cellRun.setText(PERCENTAGE_DECIMAL_FORMAT.format(entity.getMonthLimit()));
				}else if(j == 3) {
					cellRun.setText(PERCENTAGE_DECIMAL_FORMAT.format(entity.getYearLimit()));
				}
			}
		}
		
        String fileName = System.currentTimeMillis() + ".docx";
        response.reset();
        response.setContentType("application/msword");
        String dispositionValue = "attachment; filename=" + URLEncoder.encode(fileName, "UTF-8");
        response.setHeader("Content-disposition", dispositionValue);
        response.setCharacterEncoding("UTF-8");
        OutputStream output = response.getOutputStream();
        document.write(output);
        document.close();
        output.close();
	}
	
	private void addData(List<Entity> list) {
		Entity entity = new Entity("深证100", new BigDecimal("4658.12"), new BigDecimal("0.0791"), new BigDecimal("0.0160"));
		list.add(entity);
		entity = new Entity("深圳成指", new BigDecimal("10430.77"), new BigDecimal("0.0762"), new BigDecimal("0.0279"));
		list.add(entity);
		entity = new Entity("沪深300", new BigDecimal("4096.58"), new BigDecimal("0.0614"), new BigDecimal("0.0449"));
		list.add(entity);
		entity = new Entity("上证指数", new BigDecimal("3050.12"), new BigDecimal("0.0399"), new BigDecimal("0.0623"));
		list.add(entity);
		entity = new Entity("上证50", new BigDecimal("3063.22"), new BigDecimal("0.0642"), new BigDecimal("0.0657"));
		list.add(entity);
		entity = new Entity("恒生指数", new BigDecimal("28189.75"), new BigDecimal("0.0441"), new BigDecimal("0.1258"));
		list.add(entity);
		entity = new Entity("道琼斯指数", new BigDecimal("28538.44"), new BigDecimal("0.1108"), new BigDecimal("0.1469"));
		list.add(entity);
	}

	public void copyTableStyle(XWPFTable targetTable, XWPFTable sourceTable) {
		// 设置表格对齐方式
		targetTable.setTableAlignment(TableRowAlign.CENTER);
		// 设置底部边框
		targetTable.setBottomBorder(sourceTable.getBottomBorderType(), sourceTable.getBottomBorderSize(), sourceTable.getBottomBorderSpace(), sourceTable.getBottomBorderColor());
		targetTable.setInsideHBorder(sourceTable.getInsideHBorderType(), sourceTable.getInsideHBorderSize(), sourceTable.getInsideHBorderSpace(), sourceTable.getInsideHBorderColor());
		targetTable.setInsideVBorder(sourceTable.getInsideVBorderType(), sourceTable.getInsideVBorderSize(), sourceTable.getInsideVBorderSpace(), sourceTable.getInsideVBorderColor());
		targetTable.setLeftBorder(sourceTable.getLeftBorderType(), sourceTable.getLeftBorderSize(), sourceTable.getLeftBorderSpace(), sourceTable.getLeftBorderColor());
		targetTable.setRightBorder(sourceTable.getRightBorderType(), sourceTable.getRightBorderSize(), sourceTable.getRightBorderSpace(), sourceTable.getRightBorderColor());
		targetTable.setTopBorder(sourceTable.getTopBorderType(), sourceTable.getTopBorderSize(), sourceTable.getTopBorderSpace(), sourceTable.getTopBorderColor());
	}
	
	private void copyFirstRow(XWPFTable targetTable, XWPFTableRow sourceRow) {
		XWPFTableRow targetRow = targetTable.getRow(0);
		targetRow.setHeight(sourceRow.getHeight());
		for(int i = 0; i < targetRow.getTableCells().size(); i++) {
			XWPFTableCell targetCell = targetRow.getCell(i);
			XWPFTableCell sourceCell = sourceRow.getCell(i);
			targetCell.setWidth(String.valueOf(sourceCell.getWidth()));
			targetCell.setWidthType(sourceCell.getWidthType());
			// 单元格垂直对齐方式
			targetCell.setVerticalAlignment(sourceCell.getVerticalAlignment());
			if(null != sourceCell.getCTTc() && null != sourceCell.getCTTc().getTcPr()) {
				targetCell.getCTTc().addNewTcPr().setShd(sourceCell.getCTTc().getTcPr().getShd());
			}
			if(CollectionUtils.isNotEmpty(targetCell.getParagraphs())) {
				targetCell.removeParagraph(0);
			}
			if(CollectionUtils.isNotEmpty(sourceCell.getParagraphs())) {
				sourceCell.getParagraphs().stream().forEach(sourcePara -> {
					XWPFParagraph targetPara = targetCell.addParagraph();
					targetPara.setSpacingAfter(sourcePara.getSpacingAfter());
					targetPara.setSpacingBefore(sourcePara.getSpacingBefore());
					targetPara.setSpacingBetween(sourcePara.getSpacingBetween(), sourcePara.getSpacingLineRule());
					targetPara.setAlignment(sourcePara.getAlignment());
					targetPara.setIndentFromLeft(sourcePara.getIndentFromLeft());
					targetPara.setIndentFromRight(sourcePara.getIndentFromRight());
					sourcePara.getRuns().stream().forEach(sourceRun -> {
						XWPFRun targetRun = targetPara.createRun();
						targetRun.setBold(sourceRun.isBold());
						targetRun.setText(sourceRun.getText(0));
						targetRun.setFontSize(sourceRun.getFontSize());
						targetRun.setFontFamily(sourceRun.getFontFamily());
						targetRun.setColor(sourceRun.getColor());
						targetRun.setItalic(sourceRun.isItalic());
					});
				});
			}
		}		
	}
	
}
2.3 模板文件

java EasyPoi 导出word main easypoi导出word表格_java

java EasyPoi 导出word main easypoi导出word表格_ide_02

3 运行效果

java EasyPoi 导出word main easypoi导出word表格_apache_03