POI报表及百万数据导入导出方案

1、POI报表的概述

1.1 需求说明

在企业级应用开发中,Excel报表是一种最常见的报表需求。Excel报表开发一般分为两种形式:为了方便操作,基于Excel的报表批量上传数据;通过java代码生成Excel报表。

1.2 Excel的两种形式

目前世面上的Excel分为两个大的版本Excel2003和Excel2007及以上两个版本,两者之间的区别如下:
POI报表及百万数据导入导出方案_第1张图片
Excel2003是一个特有的二进制格式,其核心结构是复合文档类型的结构,存储数据量较小;Excel2007 的核心结构是 XML 类型的结构,采用的是基于 XML 的压缩方式,使其占用的空间更小,操作效率更高

1.3 常见excel操作工具

Java中常见的用来操作Excl的方式一般有2种:JXL和POI。
JXL只能对Excel进行操作,属于比较老的框架,它只支持到Excel 95-2000的版本。现在已经停止更新和维护。
Apache POI是Apache软件基金会的开源项目,由Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java语言操作Microsoft Office的功能。可对微软的Word,Excel,Ppt进行操作,包括office2003和2007,Excl2003和2007。poi现在一直有更新。所以现在主流使用POI。

1.4 POI的应用场景
  1. 数据报表生成
  2. 数据备份
  3. 数据批量上传

2、POI的入门操作

2.1 搭建环境
<dependencies>
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>4.0.1</version>
  </dependency>
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>4.0.1</version>
  </dependency>
  <dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>4.0.1</version>
  </dependency>
</dependencies>
2.2 POI结构说明
  • HSSF提供读写Microsoft Excel XLS格式档案的功能。
  • XSSF提供读写Microsoft Excel OOXML XLSX格式档案的功能。
  • HWPF提供读写Microsoft Word DOC格式档案的功能。
  • HSLF提供读写Microsoft PowerPoint格式档案的功能。
  • HDGF提供读Microsoft Visio格式档案的功能。
  • HPBF提供读Microsoft Publisher格式档案的功能。
  • HSMF提供读Microsoft Outlook格式档案的功能。
2.3 API介绍
  • Workbook: Excel的文档对象,针对不同的Excel类型分为:HSSFWorkbook(2003)和 XSSFWorkbool(2007)
  • Sheet: Excel的表单
  • Row: Excel的行
  • Cell: Excel的格子单元
  • Font: Excel字体
  • CellStyle: 格子单元样式
2.4 基本操作
2.4.1 创建Excel
@Test
public void createExcel throws Exception {
        //1.创建工作簿  HSSFWorkbook -- 2003
        Workbook wb = new XSSFWorkbook(); //2007版本
        //2.创建表单sheet
        Sheet sheet = wb.createSheet("test");
        //3.文件流
        FileOutputStream pis = new FileOutputStream("E:\\excel\\poi\\test.xlsx");
        //4.写入文件
        wb.write(pis);
        pis.close();
    }
2.4.2 创建单元格
@Test
public void createCell throws Exception {
        //创建工作簿  HSSFWorkbook -- 2003
        Workbook wb = new XSSFWorkbook(); //2007版本
        //创建表单sheet
        Sheet sheet = wb.createSheet("test");
        //创建行对象  参数:索引(从0开始)
        Row row = sheet.createRow(2);
        //创建单元格对象  参数:索引(从0开始)
        Cell cell = row.createCell(2);
        //向单元格中写入内容
        cell.setCellValue("君莫笑");
        //文件流
        FileOutputStream pis = new FileOutputStream("E:\\excel\\poi\\test1.xlsx");
        //写入文件
        wb.write(pis);
        pis.close();
    }
2.4.3 设置格式
@Test
     public void setStyle() throws Exception {
        //创建工作簿  HSSFWorkbook -- 2003
        Workbook wb = new XSSFWorkbook(); //2007版本
        //创建表单sheet
        Sheet sheet = wb.createSheet("test");
        //创建行对象  参数:索引(从0开始)
        Row row = sheet.createRow(2);
        //创建单元格对象  参数:索引(从0开始)
        Cell cell = row.createCell(2);
        //向单元格中写入内容
        cell.setCellValue("君莫笑");

        //样式处理
        //创建样式对象
        CellStyle style = wb.createCellStyle();
        style.setBorderTop(BorderStyle.THIN);//上边框
        style.setBorderBottom(BorderStyle.THIN);//下边框
        style.setBorderLeft(BorderStyle.THIN);//左边框
        style.setBorderRight(BorderStyle.THIN);//右边框
        //创建字体对象
        Font font = wb.createFont();
        font.setFontName("华文行楷"); //字体
        font.setFontHeightInPoints((short)28);//字号
        style.setFont(font);

        //行高和列宽
        row.setHeightInPoints(50);//行高
        //列宽的宽度  字符宽度
        sheet.setColumnWidth(2,31 * 256);//列宽

        //剧中显示
        style.setAlignment(HorizontalAlignment.CENTER);//水平居中
        style.setVerticalAlignment(VerticalAlignment.CENTER);//垂直居中

        //向单元格设置样式
        cell.setCellStyle(style);

        //文件流
        FileOutputStream pis = new FileOutputStream("E:\\excel\\poi\\test2.xlsx");
        //写入文件
        wb.write(pis);
        pis.close();
    }
2.4.4 插入图形
blic void createPicture() throws Exception {
        //创建工作簿  HSSFWorkbook -- 2003
        Workbook wb = new XSSFWorkbook(); //2007版本
        //创建表单sheet
        Sheet sheet = wb.createSheet("test");

        //读取图片流
        FileInputStream stream = new FileInputStream("E:\\excel\\poi\\test.jpg");
        //转化二进制数组
        byte[] bytes = IOUtils.toByteArray(stream);
        stream.read(bytes);
        //向POI内存中添加一张图片,返回图片在图片集合中的索引
        int index = wb.addPicture(bytes, Workbook.PICTURE_TYPE_JPEG);//参数一:图片的二进制数据,参数二:图片类型
        //绘制图片工具类
        CreationHelper helper = wb.getCreationHelper();
        //创建一个绘图对象
        Drawing<?> patriarch = sheet.createDrawingPatriarch();
        //创建锚点,设置图片坐标
        ClientAnchor anchor = helper.createClientAnchor();
        anchor.setRow1(0);
        anchor.setCol1(0);
        //绘制图片
        Picture picture = patriarch.createPicture(anchor, index);//图片位置,图片的索引
        picture.resize();//自适应渲染图片

        //文件流
        FileOutputStream pis = new FileOutputStream("E:\\excel\\poi\\test3.xlsx");
        //写入文件
        wb.write(pis);
        pis.close();
    }
2.4.5 加载解析Excel
	/**
	 * 读取excel并解析
	 *      sheet.getLastRowNum() : 最后一行的索引
	 *      row.getLastCellNum() : 最后一个单元格的号码
	 */
	 @Test
     public void parseExcel() throws Exception {
        //1.根据Excel文件创建工作簿
        Workbook wb = new XSSFWorkbook("E:\\excel\\poi\\test.xlsx");
        //2.获取Sheet
        Sheet sheet = wb.getSheetAt(0);//参数:索引
        //3.获取Sheet中的每一行,和每一个单元格
        for (int rowNum = 0; rowNum<= sheet.getLastRowNum() ;rowNum ++) {
            Row row = sheet.getRow(rowNum);//根据索引获取每一个行
            StringBuilder sb = new StringBuilder();
            for(int cellNum=2;cellNum< row.getLastCellNum(); cellNum ++) {
                //根据索引获取每一个单元格
                Cell cell = row.getCell(cellNum);
                //获取每一个单元格的内容
                Object value = getCellValue(cell);
                sb.append(value).append("-");
            }
            System.out.println(sb.toString());
        }
    }

    public static Object getCellValue(Cell cell) {
        //1.获取到单元格的属性类型
        CellType cellType = cell.getCellType();
        //2.根据单元格数据类型获取数据
        Object value = null;
        switch (cellType) {
            case STRING:
                value = cell.getStringCellValue();
                break;
            case BOOLEAN:
                value = cell.getBooleanCellValue();
                break;
            case NUMERIC:
                if(DateUtil.isCellDateFormatted(cell)) {
                    //日期格式
                    value = cell.getDateCellValue();
                }else{
                    //数字
                    value = cell.getNumericCellValue();
                }
                break;
            case FORMULA: //公式
                value = cell.getCellFormula();
                break;
            default:
                break;
        }
        return value;
    }
2.5 用户导入示例
/**
     * 导入Excel,添加用户
     *  文件上传:springboot
     */
    @RequestMapping(value="/user/import",method = RequestMethod.POST)
    public Result importUser(@RequestParam(name="file") MultipartFile file) throws Exception {
        //1.解析Excel
        //1.1.根据Excel文件创建工作簿
        Workbook wb = new XSSFWorkbook(file.getInputStream());
        //1.2.获取Sheet
        Sheet sheet = wb.getSheetAt(0);//参数:索引
        //1.3.获取Sheet中的每一行,和每一个单元格
        //2.获取用户数据列表
        List<User> list = new ArrayList<>();
        System.out.println(sheet.getLastRowNum());
        for (int rowNum = 1; rowNum<= sheet.getLastRowNum() ;rowNum ++) {
            Row row = sheet.getRow(rowNum);//根据索引获取每一个行
            Object [] values = new Object[row.getLastCellNum()];
            for(int cellNum=1;cellNum< row.getLastCellNum(); cellNum ++) {
                Cell cell = row.getCell(cellNum);
                Object value = getCellValue(cell);
                values[cellNum] = value;
            }
            //在User的构造函数中,根据表格值顺序设置User对应的属性值
            User user = new User(values);
            list.add(user);
        }
        //3.调用服务层批量保存用户
        userService.saveAll(list);

        return new Result(ResultCode.SUCCESS);
    }

    public static Object getCellValue(Cell cell) {
        //1.获取到单元格的属性类型
        CellType cellType = cell.getCellType();
        //2.根据单元格数据类型获取数据
        Object value = null;
        switch (cellType) {
            case STRING:
                value = cell.getStringCellValue();
                break;
            case BOOLEAN:
                value = cell.getBooleanCellValue();
                break;
            case NUMERIC:
                if(DateUtil.isCellDateFormatted(cell)) {
                    //日期格式
                    value = cell.getDateCellValue();
                }else{
                    //数字
                    value = cell.getNumericCellValue();
                }
                break;
            case FORMULA: //公式
                value = cell.getCellFormula();
                break;
            default:
                break;
        }
        return value;
    }
2.6 用户导出示例
  @RequestMapping(value = "/export/{month}", method = RequestMethod.GET)
  public void export(@PathVariable(name = "month") String month) throws Exception {
    //1.构造数据,某个月的用户数据
    List<User> list =userService.findByMonth(month);
    //2.创建工作簿
    XSSFWorkbook workbook = new XSSFWorkbook();
    //3.构造sheet
    String[] titles = {"编号", "姓名", "手机","最高学历", "国家地区","生日", "时间"};
    Sheet sheet = workbook.createSheet();
    Row row = sheet.createRow(0);
    AtomicInteger headersAi = new AtomicInteger();
    for (String title : titles) {
      Cell cell = row.createCell(headersAi.getAndIncrement());
      cell.setCellValue(title);
   }
    AtomicInteger datasAi = new AtomicInteger(1);
    Cell cell = null;
    for (User user : list) {
      Row dataRow = sheet.createRow(datasAi.getAndIncrement());
      //编号
      cell = dataRow.createCell(0);
      cell.setCellValue(user.getUserId());
      //姓名
      cell = dataRow.createCell(1);
      cell.setCellValue(user.getUsername());
      //手机
      cell = dataRow.createCell(2);
      cell.setCellValue(user.getMobile());
      //最高学历
      cell = dataRow.createCell(3);
      cell.setCellValue(user.getTheHighestDegreeOfEducation());
      //国家地区
      cell = dataRow.createCell(4);
      cell.setCellValue(user.getNationalArea());
      //生日
      cell = dataRow.createCell(7);
      cell.setCellValue(user.getBirthday());
      //时间
      cell = dataRow.createCell(12);
      cell.setCellValue(user.getCreateTime());
   }
    String fileName = URLEncoder.encode(month+"人员信息.xlsx", "UTF-8");
    response.setContentType("application/octet-stream");
    response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("ISO8859-1")));
    response.setHeader("filename", fileName);
    workbook.write(response.getOutputStream());
 }

3、模板打印

3.1 概述

自定义生成Excel报表文件还是有很多不尽如意的地方,特别是针对复杂报表头,单元格样式,字体等操作。手写这些代码不仅费时费力,有时候效果还不太理想。那怎么样才能更方便的对报表样式,报表头进行处理呢?答案是使用已经准备好的Excel模板,只需要关注模板中的数据即可。

3.2 模板打印的操作步骤
  1. 制作模版文件(模版文件的路径)
  2. 导入(加载)模版文件,从而得到一个工作簿
  3. 读取工作表
  4. 读取行
  5. 读取单元格
  6. 读取单元格样式
  7. 设置单元格内容
  8. 其他单元格就可以使用读到的样式了
3.3 制作模板

在这里插入图片描述

3.4 代码实现
  @RequestMapping(value = "/export/{month}", method = RequestMethod.GET)
  public void export(@PathVariable(name = "month") String month) throws Exception {
	//1.构造数据
	List<User> list =userService.findByMonth(month);
	//2.加载模板流数据
	Resource resource = new ClassPathResource("excel-template/demo.xlsx");
	FileInputStream fis = new FileInputStream(resource.getFile());
	//3.根据文件流,加载指定的工作簿
	XSSFWorkbook wb = new XSSFWorkbook(fis);
	//4.读取工作表
	Sheet sheet = wb.getSheetAt(0);
	//5.抽取公共的样式
	Row styleRow = sheet.getRow(2);
	CellStyle [] styles = new CellStyle[styleRow.getLastCellNum()];
	for(int i=0;i<styleRow.getLastCellNum();i++) {
		styles[i] = styleRow.getCell(i).getCellStyle();
	}
    //6.构造每行和单元格数据
    AtomicInteger datasAi = new AtomicInteger(2);
    Cell cell = null;
    for (EmployeeReportResult report : list) {
      Row dataRow = sheet.createRow(datasAi.getAndIncrement());
      //编号
      cell = dataRow.createCell(0);
      cell.setCellValue(report.getUserId());
      cell.setCellStyle(styles[0]);
      //姓名
      cell = dataRow.createCell(1);
      cell.setCellValue(report.getUsername());
      cell.setCellStyle(styles[1]);
      //手机
      cell = dataRow.createCell(2);
      cell.setCellValue(report.getMobile());
      cell.setCellStyle(styles[2]);
      //最高学历
      cell = dataRow.createCell(3);
      cell.setCellValue(report.getTheHighestDegreeOfEducation());
      cell.setCellStyle(styles[3]);
      //国家地区
      cell = dataRow.createCell(4);
      cell.setCellValue(report.getNationalArea());
      cell.setCellStyle(styles[4]);
      //生日
      cell = dataRow.createCell(7);
      cell.setCellValue(report.getBirthday());
      cell.setCellStyle(styles[7]);
      //离职时间
      cell = dataRow.createCell(12);
      cell.setCellStyle(styles[12]);
      cell.setCellValue(report.getCreateTime());
   }
    String fileName = URLEncoder.encode(month+"人员信息.xlsx", "UTF-8");
    response.setContentType("application/octet-stream");
    response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("ISO8859-1")));
    response.setHeader("filename", fileName);
    wb.write(response.getOutputStream());
 }

4、自定义工具类

(1)自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelAttribute {
  /** 对应的列名称 */
  String name() default "";
  /** 列序号 */
  int sort();
  /** 字段类型对应的格式 */
  String format() default "";
}

(2)导出工具类

@Getter
@Setter
public class ExcelExportUtil<T> {
  private int rowIndex;
  private int styleIndex;
  private String templatePath;
  private Class clazz;
  private  Field fields[];
  public ExcelExportUtil(Class clazz,int rowIndex,int styleIndex) {
    this.clazz = clazz;
    this.rowIndex = rowIndex;
    this.styleIndex = styleIndex;
    fields = clazz.getDeclaredFields();
 }
  /**
  * 基于注解导出
  */
  public void export(HttpServletResponse response,InputStream is, List<T> objs,String
fileName) throws Exception {
    XSSFWorkbook workbook = new XSSFWorkbook(is);
    Sheet sheet = workbook.getSheetAt(0);
    CellStyle[] styles = getTemplateStyles(sheet.getRow(styleIndex));
    AtomicInteger datasAi = new AtomicInteger(rowIndex);
    for (T t : objs) {
      Row row = sheet.createRow(datasAi.getAndIncrement());
      for(int i=0;i<styles.length;i++) {
        Cell cell = row.createCell(i);
        cell.setCellStyle(styles[i]);
        for (Field field : fields) {
          if(field.isAnnotationPresent(ExcelAttribute.class)){
            field.setAccessible(true);
            ExcelAttribute ea = field.getAnnotation(ExcelAttribute.class);
            if(i == ea.sort()) {
              cell.setCellValue(field.get(t).toString());
           }
         }
       }
     }
   }
    fileName = URLEncoder.encode(fileName, "UTF-8");
    response.setContentType("application/octet-stream");
    response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("ISO8859-1")));
    response.setHeader("filename", fileName);
    workbook.write(response.getOutputStream());
 }
  public CellStyle[] getTemplateStyles(Row row) {
    CellStyle [] styles = new CellStyle[row.getLastCellNum()];
    for(int i=0;i<row.getLastCellNum();i++) {
      styles[i] = row.getCell(i).getCellStyle();
   }
    return styles;
 }
}

(3)导入工具类

public class ExcelImportUtil<T> {
  private Class clazz;
  private  Field fields[];
  public ExcelImportUtil(Class clazz) {
    this.clazz = clazz;
    fields = clazz.getDeclaredFields();
 }
  /**
  * 基于注解读取excel
  */
  public List<T> readExcel(InputStream is, int rowIndex,int cellIndex) {
    List<T> list = new ArrayList<T>();
    T entity = null;
    try {
      XSSFWorkbook workbook = new XSSFWorkbook(is);
      Sheet sheet = workbook.getSheetAt(0);
      // 不准确
      int rowLength = sheet.getLastRowNum();
      System.out.println(sheet.getLastRowNum());
      for (int rowNum = rowIndex; rowNum <= sheet.getLastRowNum(); rowNum++) {
        Row row = sheet.getRow(rowNum);
        entity = (T) clazz.newInstance();
        System.out.println(row.getLastCellNum());
        for (int j = cellIndex; j < row.getLastCellNum(); j++) {
          Cell cell = row.getCell(j);
          for (Field field : fields) {
            if(field.isAnnotationPresent(ExcelAttribute.class)){
              field.setAccessible(true);
              ExcelAttribute ea =
field.getAnnotation(ExcelAttribute.class);
              if(j == ea.sort()) {
                field.set(entity, covertAttrType(field, cell));
             }
           }
         }
       }
        list.add(entity);
     }
   } catch (Exception e) {
      e.printStackTrace();
   }
    return list;
 }
  /**
  * 类型转换 将cell 单元格格式转为 字段类型
  * */
  private Object covertAttrType(Field field, Cell cell) throws Exception {
    String fieldType = field.getType().getSimpleName();
    if ("String".equals(fieldType)) {
      return getValue(cell);
   }else if ("Date".equals(fieldType)) {
      return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").parse(getValue(cell)) ;
   }else if ("int".equals(fieldType) || "Integer".equals(fieldType)) {
      return Integer.parseInt(getValue(cell));
   }else if ("double".equals(fieldType) || "Double".equals(fieldType)) {
      return Double.parseDouble(getValue(cell));
   }else {
      return null;
   }
 }
  /**
  * 格式转为String
  * @param cell
  * @return
  */
  public String getValue(Cell cell) {
    if (cell == null) {
      return "";
   }
    switch (cell.getCellType()) {
      case STRING:
        return cell.getRichStringCellValue().getString().trim();
      case NUMERIC:
        if (DateUtil.isCellDateFormatted(cell)) {
          Date dt = DateUtil.getJavaDate(cell.getNumericCellValue());
          return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(dt);
       } else {
          // 防止数值变成科学计数法
          String strCell = "";
          Double num = cell.getNumericCellValue();
          BigDecimal bd = new BigDecimal(num.toString());
          if (bd != null) {
            strCell = bd.toPlainString();
         }
          // 去除 浮点型 自动加的 .0
          if (strCell.endsWith(".0")) {
            strCell = strCell.substring(0, strCell.indexOf("."));
         }
          return strCell;
       }
      case BOOLEAN:
        return String.valueOf(cell.getBooleanCellValue());
      default:
        return "";
   }
 }
 }

5、百万数据报表解决方案

5.1 百万数据报表导出

使用Apache POI完成百万数据量的Excel报表导出。

5.1.1 思路分析

基于XSSFWork导出Excel报表,是通过将所有单元格对象保存到内存中,当所有的Excel单元格全部创建完成之后一次性写入到Excel并导出。当百万数据级别的Excel导出时,随着表格的不断创建,内存中对象越来越多,直至内存溢出。Apache Poi提供了SXSSFWork对象,专门用于处理大数据量Excel报表导出。

5.1.2 原理分析

在实例化SXSSFWork这个对象时,可以指定在内存中所产生的POI导出相关对象的数量(默认100),一旦内存中的对象的个数达到这个指定值时,就将内存中的这些对象的内容写入到磁盘中(XML的文件格式),就可以将这些对象从内存中销毁,以后只要达到这个值,就会以类似的处理方式处理,直至Excel导出完成。

5.1.3 代码实现

在原有代码的基础上替换之前的XSSFWorkbook,使用SXSSFWorkbook完成创建过程即可,但是SXSSFWorkbook不支持模板导出。

  @RequestMapping(value = "/export/{month}", method = RequestMethod.GET)
  public void export(@PathVariable(name = "month") String month) throws Exception {
    //1.构造数据,某个月的用户数据
    List<User> list =userService.findByMonth(month);
    //2.创建工作簿
    SXSSFWorkbook workbook = new SXSSFWorkbook();
    //3.构造sheet
    String[] titles = {"编号", "姓名", "手机","最高学历", "国家地区","生日", "时间"};
    Sheet sheet = workbook.createSheet();
    Row row = sheet.createRow(0);
    AtomicInteger headersAi = new AtomicInteger();
    for (String title : titles) {
      Cell cell = row.createCell(headersAi.getAndIncrement());
      cell.setCellValue(title);
   }
    AtomicInteger datasAi = new AtomicInteger(1);
    Cell cell = null;
    for (User user : list) {
      Row dataRow = sheet.createRow(datasAi.getAndIncrement());
      //编号
      cell = dataRow.createCell(0);
      cell.setCellValue(user.getUserId());
      //姓名
      cell = dataRow.createCell(1);
      cell.setCellValue(user.getUsername());
      //手机
      cell = dataRow.createCell(2);
      cell.setCellValue(user.getMobile());
      //最高学历
      cell = dataRow.createCell(3);
      cell.setCellValue(user.getTheHighestDegreeOfEducation());
      //国家地区
      cell = dataRow.createCell(4);
      cell.setCellValue(user.getNationalArea());
      //生日
      cell = dataRow.createCell(7);
      cell.setCellValue(user.getBirthday());
      //时间
      cell = dataRow.createCell(12);
      cell.setCellValue(user.getCreateTime());
   }
    String fileName = URLEncoder.encode(month+"人员信息.xlsx", "UTF-8");
    response.setContentType("application/octet-stream");
    response.setHeader("content-disposition", "attachment;filename=" + new String(fileName.getBytes("ISO8859-1")));
    response.setHeader("filename", fileName);
    workbook.write(response.getOutputStream());
 }
5.2 百万数据报表导入

使用POI基于事件模式解析百万数据的Excel文件。

5.2.1 思路分析

用户模式:加载并读取Excel时,是通过一次性的将所有数据加载到内存中再去解析每个单元格内容。当Excel数据量较大时,由于不同的运行环境可能会造成内存不足甚至OOM异常。
事件模式:它逐行扫描文档,一边扫描一边解析。由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中,这对于大型文档的解析是个巨大优势。

5.2.2 步骤分析

(1)设置POI的事件模式
  根据Excel获取文件流
  根据文件流创建OPCPackage
  创建XSSFReader对象
(2)Sax解析
  自定义Sheet处理器
  创建Sax的XmlReader对象
  设置Sheet的事件处理器
  逐行读取

5.2.3 原理分析

我们都知道对于Excel2007的实质是一种特殊的XML存储数据,那就可以使用基于SAX的方式解析XML完成Excel的读取。SAX提供了一种从XML文档中读取数据的机制。它逐行扫描文档,一边扫描一边解析。由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中,这对于大型文档的解析是个巨大优势。

5.2.4 代码实现
5.2.4.1 自定义处理器
import cn.itcast.poi.entity.PoiEntity;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.usermodel.XSSFComment;

/**
 * 自定义的事件处理器
 *  处理每一行数据读取
 *      实现接口
 */
public class SheetHandler implements XSSFSheetXMLHandler.SheetContentsHandler {

    private PoiEntity entity;
    /**
     * 当开始解析某一行的时候触发
     *      i:行索引
     */
    @Override
    public void startRow(int i) {
        //实例化对象
        if(i>0) {
            entity = new PoiEntity();
        }
    }

    /**
     * 当结束解析某一行的时候触发
     *      i:行索引
     */
    @Override
    public void endRow(int i) {
        //使用对象进行业务操作
        System.out.println(entity);
    }

    /**
     * 对行中的每一个表格进行处理
     *      cellReference: 单元格名称
     *      value:数据
     *      xssfComment:批注
     */
    @Override
    public void cell(String cellReference, String value, XSSFComment xssfComment) {
        //对对象属性赋值
        if(entity != null) {
            String pix = cellReference.substring(0,1);
            switch (pix) {
                case "A":
                    entity.setId(value);
                    break;
                case "B":
                    entity.setBreast(value);
                    break;
                case "C":
                    entity.setAdipocytes(value);
                    break;
                case "D":
                    entity.setNegative(value);
                    break;
                case "E":
                    entity.setStaining(value);
                    break;
                case "F":
                    entity.setSupportive(value);
                    break;
                default:
                    break;
            }
        }
    }
}
5.2.4.2 自定义解析
/**
* 自定义Excel解析器
*/
public class ExcelParser {
  public void parse (String path) throws Exception {
    //1.根据Excel获取OPCPackage对象
    OPCPackage pkg = OPCPackage.open(path, PackageAccess.READ);
    try {
      //2.创建XSSFReader对象
      XSSFReader reader = new XSSFReader(pkg);
      //3.获取SharedStringsTable对象
      SharedStringsTable sst = reader.getSharedStringsTable();
      //4.获取StylesTable对象
      StylesTable styles = reader.getStylesTable();
      //5.创建Sax的XmlReader对象
      XMLReader parser = XMLReaderFactory.createXMLReader();
      //6.设置处理器
      parser.setContentHandler(new XSSFSheetXMLHandler(styles,sst, new
SheetHandler(), false));
      XSSFReader.SheetIterator sheets = (XSSFReader.SheetIterator)
reader.getSheetsData();
      //7.逐行读取
      while (sheets.hasNext()) {
        InputStream sheetstream = sheets.next();
        InputSource sheetSource = new InputSource(sheetstream);
        try {
          parser.parse(sheetSource);
       } finally {
          sheetstream.close();
       }
     }
   } finally {
      pkg.close();
   }
 }
}

你可能感兴趣的:(Java,POI,报表解决方案,JAVA)