天天看点

poi导出word时内嵌表格场景,即开即用设计分析部分后端代码部分前端代码部分

poi导出word内嵌表格

  • 设计分析部分
  • 后端代码部分
    • xml文件配置
    • 实体类
    • controller层
    • 使用到的工具类
    • service层
  • 前端代码部分
    • 测试页面

设计分析部分

首先要对导出word的需求进行分析,以下展示,本项目中,导出的word格式

出于某些保密原因,某些图片已被隐藏,可能给大家看到的效果没有现实导出的效果好,请各位自行脑补,多多包涵。

为什么要使用poi?其实,设计的时候,也思考过,使用模板的形式,直接向模板中添加数据,后来发现,如果使用模板非常容易产生错版,而使用poi会更好的自定义生成某一栏,自定义某一栏的行高等,所以尝试使用poi完成该项目的需求;

废话不多说,上代码:

poi导出word时内嵌表格场景,即开即用设计分析部分后端代码部分前端代码部分

后端代码部分

xml文件配置

引入项目相关的依赖,注意,此处的版本号常见使用3.11版本,那么,如果使用3.11版本,在本套代码中,插入图片部分的操作,需要作出部分改动

<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi</artifactId>
			<version>3.17</version>
		</dependency>
		<dependency>
			<groupId>org.apache.poi</groupId>
			<artifactId>poi-ooxml</artifactId>
			<version>3.17</version>
		</dependency>
           

实体类

实体类部分,我选择的是表头的几个栏目配置为一个实体类

@Data
public class MaterialEntity {
    private String id;
    private Integer sort;//序号
    private String specifications;//规格
    private String unit;//单位
    private String num;//数量
    private Double netWeight;//净量
    private Double total;//总量
    private String remark;//备注
    private String materialNo;//物码
}
           

controller层

controller层上,提供一个接口,供导出时,页面调用

@RequestMapping(value = "/exportDesignWord",method = RequestMethod.POST)
    public void exportDesignWord(@RequestParam(value = "basicId")String basicId ,HttpServletResponse response){
        try {
            materialService.exportDesign(basicId,response);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
           

使用到的工具类

package com.exportword.utils;

import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.*;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlToken;
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps;
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;
import org.openxmlformats.schemas.drawingml.x2006.wordprocessingDrawing.CTInline;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import javax.imageio.stream.FileImageInputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.util.List;

public class ExportWord extends XWPFDocument {
    public static PathMatchingResourcePatternResolver pathMatchingResourcePatternResolver = new PathMatchingResourcePatternResolver();
    private XWPFDocument doc;
    public ExportWord() {
        super();
    }
    public ExportWord(InputStream in) throws IOException {
        super(in);
    }
    public ExportWord(HttpServletResponse response, String fileName) {
        try {
            response.setContentType("application/vnd.ms-excel;charset=utf-8");
            response.setHeader("Content-Disposition",
                    "attachment;filename=" + new String((fileName).getBytes(), "iso-8859-1"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    public ExportWord(OPCPackage pkg) throws IOException {
        super(pkg);
    }

    public  XWPFDocument getDoc(){
        return this.doc;
    }


    public  void mergeCellsHorizontal(XWPFTable table, int row, int fromCell, int toCell) {
        for (int cellIndex = fromCell; cellIndex <= toCell; cellIndex++) {
            XWPFTableCell cell = table.getRow(row).getCell(cellIndex);
            if ( cellIndex == fromCell ) {
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.RESTART);
            } else {
                cell.getCTTc().addNewTcPr().addNewHMerge().setVal(STMerge.CONTINUE);
            }
        }
    }
    /**
     * @描述: 跨行并单元格
     */
    public void mergeCellsVertically(XWPFTable table, int col, int fromRow, int toRow) {
        for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++) {
            XWPFTableCell cell = table.getRow(rowIndex).getCell(col);
            if ( rowIndex == fromRow ) {
                // The first merged cell is set with RESTART merge value
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.RESTART);
            } else {
                // Cells which join (merge) the first one, are set with CONTINUE
                cell.getCTTc().addNewTcPr().addNewVMerge().setVal(STMerge.CONTINUE);
            }
        }
    }

    /**
     *  合并单元格,从第几行第几列合并到第几行到第几列
     */
    public void mergeCellsComplex(XWPFTable table, int fromRow, int fromCol, int toRow,int toCol) {
        int rows = toRow - fromRow;
        int cols = toCol - fromCol;
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                mergeCellsVertically(table,fromCol+j,fromRow,toRow);
            }
        }
        for (int i = 0; i < cols; i++) {
            for (int j = 0; j < rows; j++) {
                mergeCellsHorizontal(table,fromRow+j,fromCol,toCol);
            }
        }
    }
    /**
     * @描述: 设置标题
     */
    public void setTitle(XWPFParagraph titleParagraph, String title) {
        titleParagraph.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun titleParagraphRun = titleParagraph.createRun();
        titleParagraphRun.setText(title);
        titleParagraphRun.setColor("000000");
        titleParagraphRun.setFontSize(22);
        titleParagraphRun.setUnderline(UnderlinePatterns.DOUBLE);
        setFontFamily(titleParagraphRun,"黑体");
    }
    /**
     * @描述: 设置字体
     */
    public void setFontFamily(XWPFRun run,String fontFamily) {
        CTRPr rpr = run.getCTR().getRPr();
        CTFonts fonts = rpr.addNewRFonts();
        fonts.setAscii(fontFamily);
        fonts.setEastAsia(fontFamily);
        fonts.setHAnsi(fontFamily);
    }
    /**
     * @描述: 换行
     */
    public void getWrap(ExportWord document) {
        XWPFParagraph paragraph = document.createParagraph();
        XWPFRun paragraphRun = paragraph.createRun();
//		paragraphRun.addBreak();
    }
    /**
     * createTable
     */
    public XWPFTable createTable(ExportWord document, int rows, int cols) {
        XWPFTable infoTable = document.createTable(rows, cols);
        CTTbl ttbl = infoTable.getCTTbl();
        CTTblPr tblPr = ttbl.getTblPr() == null ? ttbl.addNewTblPr() : ttbl.getTblPr();
        CTTblWidth tblWidth = tblPr.isSetTblW() ? tblPr.getTblW() : tblPr.addNewTblW();
        CTJc cTJc = tblPr.addNewJc();
        cTJc.setVal(STJc.Enum.forString("center"));
        tblWidth.setW(new BigInteger("8300"));
        tblWidth.setType(STTblWidth.DXA);
        return infoTable;
    }
    /**
     * @描述: 设置列宽
     */
    public void setColWidth(XWPFTable table) {
        List<XWPFTableRow> rows = table.getRows();
        XWPFTableRow row;
        List<XWPFTableCell> cells;
        XWPFTableCell cell;
        int rowSize = rows.size();
        int cellSize;
        for (int i = 0; i < rowSize; i++) {
            row = rows.get(i);
            cells = row.getTableCells(); // 获得单元格
            cellSize = cells.size();
            // 分列设置单元格宽度或者说列宽
            row.setHeight(400); // 设置行高
            for (int j = 0; j < cellSize; j++) {
                cell = cells.get(j);
                CTTcPr cellPr = cell.getCTTc().addNewTcPr();
                CTTblWidth cellw = cellPr.addNewTcW();
                cellw.setType(STTblWidth.DXA);
                cellPr.addNewTcW().setW(BigInteger.valueOf(500));
            }

        }
    }
    /**
     * @描述: 获取表格的列(不包括合并单元格的)
     */
    public int getCellOfRow(XWPFTable table){
        List<XWPFTableRow> rows = table.getRows();
        if(rows.size()>0){
            return rows.get(0).getTableCells().size();
        }else{
            return 0;
        }
    }
    /**
     * @描述: 给表格赋值
     */
    public void setTableTextLeft(XWPFTable table,int row,int col,String msg) {
        setTableText(table,row,col,msg,ParagraphAlignment.LEFT);
    }
    public void setTableTextRight(XWPFTable table,int row,int col,String msg) {
        setTableText(table,row,col,msg,ParagraphAlignment.RIGHT);
    }
    public void setTableTextCenter(XWPFTable table,int row,int col,String msg) {
        setTableText(table,row,col,msg,ParagraphAlignment.CENTER);
    }
    public void setTableTextBoth(XWPFTable table,int row,int col,String msg) {
        setTableText(table,row,col,msg,ParagraphAlignment.BOTH);
    }
    public void setTableText(XWPFTable table,int row,int col,String msg,ParagraphAlignment alignment) {
        List<XWPFTableCell> tableCells = table.getRow(row).getTableCells();
        XWPFTableCell cell= tableCells.get(col);
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        XWPFParagraph table_p = cell.getParagraphs().get(0);
        table_p.setAlignment(alignment);
        if (msg.indexOf("\n")>0){
            String[] split = msg.split("\n");
            for (int i = 0; i < split.length; i++) {
                XWPFRun table_run= table_p.createRun();
                table_run.setText(split[i]);
                table_run.setColor("000000");
                table_run.setFontSize(10);
                if (i<split.length-1){
                    table_run.addBreak();//换行
                }
                setFontFamily(table_run,"宋体");
            }
        }else {
            XWPFRun table_run= table_p.createRun();
            table_run.setText(msg);
            table_run.setColor("000000");
            table_run.setFontSize(10);
            setFontFamily(table_run,"宋体");
        }
    }

    public void setTableTextAndImg(XWPFTable table,int row,int col,String picName,int imgWidth,int imgHeight,String msg) {
        List<XWPFTableCell> tableCells = table.getRow(row).getTableCells();
        XWPFTableCell cell= tableCells.get(col);
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        XWPFParagraph table_p = cell.getParagraphs().get(0);
        table_p.setAlignment(ParagraphAlignment.LEFT);
        try {
            InputStream inputStream = pathMatchingResourcePatternResolver.getResource("static/images/" + picName).getInputStream();
            table_p.createRun().addPicture(inputStream, XWPFDocument.PICTURE_TYPE_PNG, picName, Units.toEMU(imgWidth), Units.toEMU(imgHeight));
        } catch (InvalidFormatException | IOException e) {
            e.printStackTrace();
        }
        XWPFRun table_run = table_p.createRun();
        if (msg.indexOf("\n")>0){
            String[] split = msg.split("\n");
            for (int i = 0; i < split.length; i++) {
                table_run.setText(split[i]);
                table_run.setColor("000000");
                table_run.setFontSize(10);
                if (i<split.length-1){
                    table_run.addBreak();//换行
                }
                setFontFamily(table_run,"宋体");
            }
        }else {
            table_run.setText(msg);
            table_run.setColor("000000");
            table_run.setFontSize(10);
            setFontFamily(table_run,"宋体");
        }
    }

    /**
     * setTableText
     * @描述: 给表格赋值并且设置样式
     */
    public void setTableText(XWPFTable table,int row,int col,String msg, String style) {
        List<XWPFTableCell> tableCells = table.getRow(row).getTableCells();
        XWPFTableCell cell= tableCells.get(col);
        cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        XWPFParagraph table_p = cell.getParagraphs().get(0);
        table_p.setAlignment(ParagraphAlignment.CENTER);
        XWPFRun table_run= table_p.createRun();
        table_run.setText(msg);
        table_run.setColor("000000");
        table_run.setFontSize(10);
        setFontFamily(table_run,"宋体");

        if("bold".equals(style)){
            table_run.setBold(true);
        }
        if("boldBig".equals(style)){
            table_run.setFontSize(14);
            table_run.setBold(true);
        }
    }
    /**
     *
     * image2byte
     * @描述: 将图片转换成字节保存到byte数组
     */
    public byte[] image2byte(String path) {
        byte[] data = null;
        FileImageInputStream input = null;
        try {
            input = new FileImageInputStream(new File(path));
            ByteArrayOutputStream output = new ByteArrayOutputStream();
            byte[] buf = new byte[1024];
            int numBytesRead = 0;
            while ((numBytesRead = input.read(buf)) != -1) {
                output.write(buf, 0, numBytesRead);
            }
            data = output.toByteArray();
            output.close();
            input.close();
        } catch (FileNotFoundException ex1) {
            ex1.printStackTrace();
        } catch (IOException ex1) {
            ex1.printStackTrace();
        }
        return data;
    }

}

           

service层

service层其实是一个绘制表格的过程,以及遍历数据list,用数据填充表格的过程

@Service
public class MaterialService {
    public void exportDesign(String name, HttpServletResponse response) {
    //此处是查询出的实体类型list,用来遍历填充表单内容
        List<MaterialEntity> listQuery = findListQuery(19);
        //使用填充的数据为假数据,到正式使用时,请自行写接口获取到一个list
        exportWord(response,listQuery);
    }

    //此处为造假数据的方法
    private List<MaterialEntity> findListQuery(int dataTolRows) {
        List<MaterialEntity> resultList = new ArrayList<MaterialEntity>();
        for (int i = 0; i < dataTolRows; i++) {
            MaterialEntity materialEntity = new MaterialEntity();
            materialEntity.setId(UUID.randomUUID().toString());
            materialEntity.setSort(i+1);
            materialEntity.setSpecifications("无缝钢管 Sch160 PE Φ21.3×4.78  GB/T 6479-20"+i);
            materialEntity.setUnit("t");
            materialEntity.setNetWeight((double) (((int)(Math.random()*10000))/100));
            materialEntity.setTotal((double) (((int)(Math.random()*10000))/100));
            materialEntity.setRemark("ABCD"+"-"+i);
            resultList.add(materialEntity);
        }
        return resultList;
    }

//正式开始导出数据
    private void exportWord(HttpServletResponse response, List<MaterialEntity> listQuery) {
        System.out.println(listQuery);
        OutputStream out = null;
        try {
            // 获取输出流
            out = response.getOutputStream();
            
//start创建表格部分开始
            // Word名字 后缀必须输docx
            ExportWord document = new ExportWord(response,"文件名.docx");
            //根据页面设计的格式,填入数据时只填入的19行数据
            //除19行数据外,2行为表最底端空余的两行,并插入的说明内容
            //7行为表头及上方合并的单元格大块,所以,共创建19+9=28行表格;
            int rows = listQuery.size() + 9;//7+2   19+9=28 建立28行12列的表
            //12列,可以参照表格最下方的一行表格,上方表格多少都有单元格合并,无法直观看出12行单元格
            XWPFTable table_base= document.createTable(document, rows,12);
//end创建表格部分结束



//start此处开始,进行单元格合并,合并成导出文件时,所需的表格样式
            //设置表头信息
            document.mergeCellsComplex(table_base,0,0,6,2);
            document.mergeCellsComplex(table_base,0,3,2,7);
            document.mergeCellsComplex(table_base,2,3,6,7);
            document.mergeCellsHorizontal(table_base,0,8,11);
            document.mergeCellsHorizontal(table_base,1,8,11);
            document.mergeCellsHorizontal(table_base,2,8,11);
            document.mergeCellsHorizontal(table_base,3,8,11);
            document.mergeCellsHorizontal(table_base,4,8,11);
            document.mergeCellsHorizontal(table_base,5,8,10);
            //设置实体数据信息
            document.mergeCellsHorizontal(table_base,6,1,5);
            document.mergeCellsHorizontal(table_base,6,10,11);
//end单元格合并结束,此时的表格基本与导出表格的外形相同


//start此处开始,进行单元格部分数据填充
			//首先填充上方合并的大单元格部分
			//shiyou1.png为本地存放的一张图片,用于插入表中,作为页面上方的logo显示(已打马部分)
            document.setTableTextAndImg(table_base,0,0,"shiyou1.png",130,40," \n工程设计证书:A13\n" +
                    "              924\n工程:150004-key\n      专业   HG   ");
            document.setTableTextCenter(table_base,0,3,"材料表");
            document.setTableTextCenter(table_base,2,3,"AAA\n改(成本部分)\n");
            document.setTableTextLeft(table_base,0,8,"项目号:");
            document.setTableTextLeft(table_base,1,8,"文件号:");
            document.setTableTextLeft(table_base,2,8,"CADD号:");
            document.setTableTextLeft(table_base,3,8,"设计阶段:施工图");
            document.setTableTextLeft(table_base,4,8,"日期:2020.");
            document.setTableTextCenter(table_base,5,8,"第 1 页 共 3 页");
            document.setTableTextCenter(table_base,5,11,"0 版");
            //填充实体表头
            document.setTableTextCenter(table_base,6,0,"序号");
            document.setTableTextCenter(table_base,6,1,"名称、规格及标准号");
            document.setTableTextCenter(table_base,6,6,"单位");
            document.setTableTextCenter(table_base,6,7,"数量");
            document.setTableTextCenter(table_base,6,8,"净量");
            document.setTableTextCenter(table_base,6,9,"总量");
            document.setTableTextCenter(table_base,6,10,"备注");
            //遍历实体数据填充
            for (int i = 0; i < listQuery.size(); i++) {
                //设置实体数据信息
                document.mergeCellsHorizontal(table_base,7+i,1,5);
                document.mergeCellsHorizontal(table_base,7+i,10,11);

                MaterialEntity entity = listQuery.get(i);
                Integer sort = entity.getSort();
                document.setTableTextCenter(table_base,7+i,0,StringUtils.isEmpty(sort)?"":sort.toString());
                String specifications = entity.getSpecifications();
                document.setTableTextCenter(table_base,7+i,1,StringUtils.isEmpty(specifications)?"":specifications);
                String unit = entity.getUnit();
                document.setTableTextCenter(table_base,7+i,6,StringUtils.isEmpty(unit)?"":unit);
                String num = entity.getNum();
                document.setTableTextCenter(table_base,7+i,7,StringUtils.isEmpty(num)?"":num);
                Double netWeight = entity.getNetWeight();
                document.setTableTextCenter(table_base,7+i,8,StringUtils.isEmpty(netWeight)?"":netWeight.toString());
                Double total = entity.getTotal();
                document.setTableTextCenter(table_base,7+i,9,StringUtils.isEmpty(total)?"":total.toString());
                String remark = entity.getRemark();
                document.setTableTextCenter(table_base,7+i,10,StringUtils.isEmpty(remark)?"":remark);
            }
//end数据填充部分结束

            document.setColWidth(table_base);
            //200
//            document.createTable()
            document.write(out);
        } catch (Exception e) {
            e.printStackTrace();
        }finally{
            try {
                out.flush();
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
           

前端代码部分

测试页面

写了一个测试页面,供调用接口,进行测试

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
    <title>首页</title>
    <link rel="stylesheet" type="text/css" href="/thirdparty/jquery-easyui-1.5.5.4/themes/default/easyui.css">
    <link rel="stylesheet" type="text/css" href="/thirdparty/jquery-easyui-1.5.5.4/themes/icon.css">
    <link rel="stylesheet" type="text/css" href="/thirdparty/jquery-easyui-1.5.5.4/themes/color.css">
    <script type="text/javascript" src="/thirdparty/jquery-easyui-1.5.5.4/jquery.min.js"></script>
    <script type="text/javascript" src="/thirdparty/jquery-easyui-1.5.5.4/jquery.easyui.min.js"></script>
    <script type="text/javascript" src="/thirdparty/jquery-easyui-1.5.5.4/locale/easyui-lang-zh_CN.js"></script>
    <script type="text/javascript" src="/thirdparty/jquery-easyui-1.5.5.4/easyui-msg.js"></script>
    <script type="text/javascript" src="thirdparty/jquery-easyui-1.5.5.4/md5.js" type="text/javascript"></script>
</head>
<body>
<form id="download_word" method="post" action="/word/exportDesignWord">
    <input  name="name" id="download_word_name">
    <button onclick="exportMaterial">下载</button>
</form>
<script>
    function exportMaterial() {
        $("#download_word").submit();
    }
</script>
</body>
</html>
           

项目中导出了两张不同类型表格,导出第二张及以上表格时,需要类似于第一张表格的操作,在第一张下面,继续创建表格,然后遍历填充即可,deomo中仅提供了导出一张表格的方法,后续将对代码进行整理后,更新导出多套表格的代码;

以上部分不为项目中正式使用的代码,仅为demo测试,但基本用法已全部展示,根据自己项目的实际需要,可进行相应的调节。

如果觉得笔者写的不错,欢迎点赞,转载,转载时请注明出处,谢谢。

继续阅读