如何优雅的导出word,之前在做项目的时候,有导出word的需求。当时解决方案是poi导出+模板导出,需要修改word的xml格式文件,还有一些模板导出的规则,xml编辑起来非常不方便。稍有不慎就会导致导出的文件无法打开或损坏。
最近又遇到了word导出的需求,而且是多表格的内容,并且表格的行列不定,显然使用poi做会非常困难,为此特别记录一下。
依赖
查找了很多资料,发现可以使用Itext导出word(Itext主要用于导出pdf,但也可以操作word),导出word需要以下依赖。
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.lowagie</groupId>
<artifactId>itext-rtf</artifactId>
<version>2.1.7</version>
</dependency>
载入依赖之后就可以愉快的敲代码了。
因为业务设计到多表格,并且是列行数量不定的,所以我自写了几个工具类去生成表格对象,通过传参来控制表格的属性。
工具类
1.TableUtil
public class TableUtil {
//默认表格全宽
private static final int tableWith = 100;
/**
* 获得word表格
* @param cols 表头第一行内容
* @param rows 表格中内容
* @return
* @throws Exception
*/
public static Table createTable(List<String> cols,List<List<String>> rows) throws Exception{
Table table = new Table(cols.size());
table.setBorderWidth(1);//设置表格边框
table.setBorderColor(Color.BLACK);//设置表格边框颜色
//表格宽度,默认100% 到顶
table.setWidth(tableWith);
table.setPadding(0);// 衬距,看效果就知道什么意思了
table.setSpacing(0);// 即单元格之间的间距
//设置第一行,也就是表头
for (String col : cols) {
table.addCell(CellUtil.createCell(col, Element.ALIGN_CENTER,Element.ALIGN_CENTER,true));
}
//结束表头
table.endHeaders();
for (List<String> row : rows) {
for (String content : row) {
table.addCell(CellUtil.createCell(content,Element.ALIGN_CENTER,Element.ALIGN_CENTER,false));
}
}
return table;
}
}
值得注意的是
table.addCell()
这个方法会根据cell内容从左到右依次插入单元格,当一行的插入的内容超过table初始设定的列长度后,自动添加一行并插入接下来的单元格。所以表头内容和行内容需要按顺序对应并且 cols的长度和rows内List的长度一致。
2.CellUtil
public class CellUtil {
/**
* 生成单元格
* @param content
* @param horizontalAlignment 水平对齐方式
* @param verticalAlignment 垂直对齐方式
* @param isHead
* @return
*/
public static Cell createCell(String content,int horizontalAlignment,int verticalAlignment,boolean isHead){
Cell cell = new Cell(content);
if (isHead){
cell.setHeader(true);
}
cell.setHorizontalAlignment(horizontalAlignment);
cell.setVerticalAlignment(verticalAlignment);
return cell;
}
/**
* 获得跨行/列的单元格
* @param colspan
* @param rowspan
* @param content
* @param horizontalAlignment
* @param verticalAlignment
* @param isHead
* @return
*/
public static Cell createCell(int colspan,int rowspan,String content,int horizontalAlignment,int verticalAlignment,boolean isHead){
Cell cell = new Cell(content);
if(isHead){
cell.setHeader(true);
}
cell.setHorizontalAlignment(horizontalAlignment);
cell.setVerticalAlignment(verticalAlignment);
cell.setColspan(colspan);
cell.setRowspan(rowspan);
return cell;
}
}
这边没什么好说的,主要是单元格可以设置对齐方式,字体等。
3.FontUtil
public class FontUtil {
private static Font defultChinese;
private static RtfFont titleFont;
private static Font questionTitle;
public static Font getDefultChinese() throws Exception{
defultChinese = new Font(Font.NORMAL, 12, Font.NORMAL, Color.black);
return defultChinese;
}
public static Font getTitleFont() {
titleFont = new RtfFont("宋体", 21, Font.BOLD, Color.BLACK);
return titleFont;
}
public static Font getQuestionTitle() {
questionTitle = new Font(Font.NORMAL, 12, Font.BOLD, new Color(0, 0, 0));
return questionTitle;
}
}
这边是自定义的一些字体,可以按需求自己多设置几种字体,需要用的时候直接通过这个util对象获取就可以了,比较方便。
写成util类是为了让业务代码变得简洁一些,因为itext的文本插入直接插入设置字体样式什么的就可以了,但是表格插入比较麻烦,所以封装一下可以让业务代码变简洁。
例程
直接看例子,应该能很直观的看出cols和rows的关系
public class Test {
public static void main(String[] args) {
Document document = new Document(PageSize.A4);
try {
RtfWriter2.getInstance(document,new FileOutputStream("E:/word.doc"));
document.open();
//写一个标题
//添加文本内容就直接这么添加即可
document.add(new Paragraph("自定义的表格", FontUtil.getTitleFont()));
//设置表格的内容
List<String> cols = new ArrayList<>();
//三列的表格
for (int i = 0; i < 3; i++) {
cols.add("第"+i+"列");
}
List<List<String>> rows = new ArrayList<>();
//五行内容
for (int i = 0; i < 5; i++) {
//三列 必须和cols对应
List<String> row = new ArrayList<>();
for (int j = 0; j < 3; j++) {
row.add("第"+i+"行,第"+j+"列");
}
rows.add(row);
}
//表格生成
Table table = TableUtil.createTable(cols,rows);
document.add(table);//直接添加表格
document.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
输出的文件截图。
上面例子是直接把文件输出到本地E盘下了,如果需要输出到浏览器,需要在这一行把FileOutputStream替换成ServletOutputStream就可以了。
RtfWriter2.getInstance(document,new FileOutputStream("E:/word.doc"));
//替换成
RtfWriter2.getInstance(document,response.getOutputStream());
值得注意的是,一定要在这一行设置输出流了之后,再执行document.open()方法才能对文件进行操作,如果RtfWriter2.getInstance再document.close()之后执行,输出的会是一个空文件。