Java POI操作Excel(User Model)

该文章为本系列的第一篇
第二篇为 : Java POI操作Excel(Event Model)
第三篇为 : Java POI操作Excel(Event User Model)
第四篇为 : 使用POI封装一个轻量级Excel解析框架

前言

在B端系统中,通常都会提供Excel导入导出数据的功能.所以使用Java对Excel进行操作是每个Java Web工程师必备的技能.而在Java领域,Apache POI是优秀的Excel操作库.这篇文章开始,我们就使用POI一步一步的进行Excel的操作,到最后自制一个简单的基于POI的Excel解析框架.

POI简介

POI-HSSF and POI-XSSF - Java API To Access Microsoft Excel Format Files

HSSF is the POI Project's pure Java implementation of the Excel '97(-2007) file format. XSSF is the POI Project's pure Java implementation of the Excel 2007 OOXML (.xlsx) file format.

POI针对目前存在的两种版本的Excel有不同的实现方式.而对于相同版本的Excel又分为Dom解析和Sax解析两种.而这两种在内存使用,性能各方面均有不同.官方文档中给出了具体的比较,如下图前四列.第五列则是对Excel2007版本提供的节约内存的导出方式进行了介绍.

Java POI操作Excel(User Model)_第1张图片
poi_api_type.png

POI UserModel

根据上面的表格我们可以看到,所谓的UserModel实际上就类似于Dom方式的解析.学过Xml解析的朋友都会知道所谓的Dom解析就是将文件全部读入内存,对文件内部的结构进行建模成一颗Dom树的过程.接下来我们看一下POI对Excel的建模的Dom树是什么结构的.

POI 官网Demo

http://poi.apache.org/spreadsheet/quick-guide.html

官网中对这个页面的标题是 Busy Developers' Guide to HSSF and XSSF Features

所以我们通过这个网页Demo的学习,即可完成对POI的入门.

快速入门

开发环境

Java版本 :1.8.0_40
Maven版本:3.3.9
POI版本 :3.15

创建一个工作簿(Workbook)

POI中使用Workbook对Excel对象进行建模.

public class Demo01 {
    public static void main(String[] args) throws Exception {
        Workbook excel1997 = new HSSFWorkbook(); // excel 1997
        FileOutputStream fileOut = new FileOutputStream("workbook.xls");
        excel1997.write(fileOut);
        fileOut.close();

        Workbook excel2007 = new XSSFWorkbook(); // excel 2007
        fileOut = new FileOutputStream("workbook.xlsx");
        excel2007.write(fileOut);
        fileOut.close();
    }
}

打开创建的Excel

Java POI操作Excel(User Model)_第2张图片
empty-excel.png

Workbook对象是UserModel解析方式中整个Dom树的根对象.而Workbook类本身是一个接口,规范了Workbook对象的基本功能.所以除非我们是要使用特定版本Excel的特性,一般都是面向Workbook接口编程.

创建一个工作表(sheet)

public static void main(String[] args) throws Exception {
        Workbook wb = new HSSFWorkbook();
        Sheet sheet1 = wb.createSheet("new sheet");
        Sheet sheet2 = wb.createSheet("second sheet");

        // Note that sheet name is Excel must not exceed 31 characters
        // and must not contain any of the any of the following characters:
        // 0x0000
        // 0x0003
        // colon (:)
        // backslash (\)
        // asterisk (*)
        // question mark (?)
        // forward slash (/)
        // opening square bracket ([)
        // closing square bracket (])

        // You can use org.apache.poi.ss.util.WorkbookUtil#createSafeSheetName(String nameProposal)}
        // for a safe way to create valid names, this utility replaces invalid characters with a space (' ')
        String safeName = WorkbookUtil.createSafeSheetName("[O'Brien's sales*?]"); // returns " O'Brien's sales   "
        Sheet sheet3 = wb.createSheet(safeName);

        FileOutputStream fileOut = new FileOutputStream("workbook02.xls");
        wb.write(fileOut);
        fileOut.close();
    }

打开创建的Excel

Java POI操作Excel(User Model)_第3张图片
create-sheet.png

Sheet对象是Workbook对象下的一级对象,代表工作表.而通过上面的程序Demo我们已经可以知道在创建Sheet的时候,要注意的工作表的命名约束.以及如何使用安全的方式创建工作表.

创建单元格

public static void main(String[] args) throws Exception {
        Workbook wb = new HSSFWorkbook();
        //Workbook wb = new XSSFWorkbook();
        CreationHelper createHelper = wb.getCreationHelper();
        Sheet sheet = wb.createSheet("new sheet");

        // Create a row and put some cells in it. Rows are 0 based.
        Row row = sheet.createRow((short)0);
        // Create a cell and put a value in it.
        Cell cell = row.createCell(0);
        cell.setCellValue(1);

        // Or do it on one line.
        row.createCell(1).setCellValue(1.2);
        row.createCell(2).setCellValue(
                createHelper.createRichTextString("This is a string"));
        row.createCell(3).setCellValue(true);

        // Write the output to a file
        FileOutputStream fileOut = new FileOutputStream("workbook03.xls");
        wb.write(fileOut);
        fileOut.close();
 }

打开创建的Excel

Java POI操作Excel(User Model)_第4张图片
create-cell.png

通过代码我们可以知道Sheet下一级的对象不是Cell而是Row,而Row的下一级对象是Cell.在部分代码很好理解.我们重点介绍一下这个RichTextString.Excel中的字符串,都会存在一个Table(数组)中,而在解析Excel的时候我们会发现解析到的文本类型的单元格的value是数字,而这个数字实际上就是该文本在RichText Table中的下标.

小结

至此,我们了解了POI对Excel操作的UserModel时Dom树的整体结构.如下图
而我们在解析Excel的时候,即可按照这种方式,从上到下,对我们需要的数据进行解析.


Java POI操作Excel(User Model)_第5张图片
usermodel-domtree.png

解析

要解析的Excel

Java POI操作Excel(User Model)_第6张图片
to-parse.png

编码

public static void main(String[] args) throws Exception {
    InputStream inp = null;
    try {
        inp = new FileInputStream("workbook04.xls");
        Workbook wb = WorkbookFactory.create(inp);
        Sheet sheet = wb.getSheetAt(0);
        Iterator rowIterator = sheet.rowIterator();
        while (rowIterator.hasNext()) {
            Row r = rowIterator.next();
            if (r == null) {
                System.out.println("Empty Row");
                continue;
            }
            for (int i = r.getFirstCellNum(); i < r.getLastCellNum(); i++) {
                Cell cell = r.getCell(i);
                String cellValue = "";
                switch (cell.getCellType()) {
                    case Cell.CELL_TYPE_STRING:
                        cellValue = cell.getRichStringCellValue().getString();
                        break;
                    case Cell.CELL_TYPE_NUMERIC:
                        if (DateUtil.isCellDateFormatted(cell)) {
                            cellValue = cell.getDateCellValue().toString();
                        } else {
                            cellValue = String.valueOf(cell.getNumericCellValue());
                        }
                        break;
                    case Cell.CELL_TYPE_BOOLEAN:
                        cellValue = String.valueOf(cell.getBooleanCellValue());
                        break;
                    case Cell.CELL_TYPE_FORMULA:
                        cellValue = String.valueOf(cell.getCellFormula());
                        break;
                    case Cell.CELL_TYPE_BLANK:
                        break;
                    default:
                }
                System.out.println("CellNum:" + i + " => CellValue:" + cellValue);
            }
        }
    } finally {
        if (inp != null) {
            inp.close();
        }
    }
}

输出结果

CellNum:0 => CellValue:1.0
CellNum:1 => CellValue:Sun Jul 16 00:00:00 CST 2017
CellNum:2 => CellValue:This is a String

小结

  • 使用WorkbookFactory来创建Workbook.从而不引入具体的Workbook实现.达到解耦的效果
  • 遍历Row和Cell的时候既可以使用Iterator,也可以使用for循环的方式.其中实际有数据的是获取到的最后一行(或一列)的前一行(一列)
  • 针对不同类型的CellType,取值方式并不相同.要使用恰当的方式.

总结

这篇文章作为Excel解析的第一篇,对POI进行了简单的介绍.然后通过几个简单小例子对POI的UserModel进行了分析与讲解.总体来说,UserModel方式解析Excel比较简单.但是作为代价,这种方式对内存的占用是不小的.这也是我们在对技术选型时要考虑的因素.

本文只作为Excel解析的入门,并不能涉及UserModel的方方面面,更多内容还需要去官网查看Demo进行学习.而使用文档进行学习也是程序员的必备技能之一.需要我们不断的训练自己这方面的能力.

参考文档

《Busy Developers' Guide to HSSF and XSSF Features》

你可能感兴趣的:(Java POI操作Excel(User Model))