优雅的导出多表格word

优雅的导出多表格word

Posted by catmai on April 28, 2020

如何优雅的导出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();
        }
    }
}

输出的文件截图。

image-1

上面例子是直接把文件输出到本地E盘下了,如果需要输出到浏览器,需要在这一行把FileOutputStream替换成ServletOutputStream就可以了。

RtfWriter2.getInstance(document,new FileOutputStream("E:/word.doc"));

//替换成
RtfWriter2.getInstance(document,response.getOutputStream());

值得注意的是,一定要在这一行设置输出流了之后,再执行document.open()方法才能对文件进行操作,如果RtfWriter2.getInstance再document.close()之后执行,输出的会是一个空文件。