Excel学习

这篇具有很好参考价值的文章主要介绍了Excel学习。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

学习链接

EasyExcel实现Excel文件导入导出 - 很详细

EasyPoi实现excel文件导入导出 - 很详细

Excel文件导入导出 - 专栏

【狂神说Java】POI技术详解

Excel

在企业级应用开发中,Excel报表是一种最常见的报表需求。Excel报表开发一般分为两种形式:

  • 为了方便操作,基于Excel的报表批量上传数据
  • 通过java代码生成Excel报表。

1. Excel的两种形式

目前世面上的Excel分为两个大的版本Excel2003和Excel2007及以上两个版本,两者之间的区别如下:

Excel学习,# 工具类,excel

Excel2003是一个特有的二进制格式,其核心结构是复合文档类型的结构,存储数据量较小;Excel2007 的核心结构是 XML 类型的结构,采用的是基于 XML 的压缩方式,使其占用的空间更小,操作效率更高

2. 常见excel操作工具

Java中常见的用来操作Excl的方式一般有2种:JXL和POI。

  • JXL只能对Excel进行操作,属于比较老的框架,它只支持到Excel 95-2000的版本。现在已经停止更新和维护。
  • POI是apache的项目,可对微软的Word,Excel,Ppt进行操作,包括office2003和2007,Excl2003和2007。poi现在一直有更新。所以现在主流使用POI。

3.POI

1. POI的概述

Apache POI是Apache软件基金会的开源项目,由Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java语言操作Microsoft Office的功能。

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

导入依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
                             http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cn.itcast</groupId>
    <artifactId>poi-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <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>
</project>
1.使用POI创建excel
/**
 * 使用POI创建excel
 */
public class PoiTest01 {


    public static void main(String[] args) throws Except   ion {
        //1.创建工作簿  HSSFWorkbook -- 2003
        Workbook wb = new XSSFWorkbook(); //2007版本
        //2.创建表单sheet
        Sheet sheet = wb.createSheet("test");
        //3.文件流
        FileOutputStream fos = new FileOutputStream("E:\\excel\\poi\\test.xlsx");
        //4.写入文件
        wb.write(fos);
        fos.close();
    }
}
2.创建单元格写入内容
/**
 * 创建单元格写入内容
 */
public class PoiTest02 {


    public static void main(String[] args) 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();
    }
}
3.单元格样式处理
/**
 * 单元格样式处理
 */
public class PoiTest03 {


    public static void main(String[] args) 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();
    }
}
4.插入图片
/**
 * 插入图片
 */
public class PoiTest04 {


    public static void main(String[] args) throws Exception {
        //创建工作簿  HSSFWorkbook -- 2003
        Workbook wb = new XSSFWorkbook(); //2007版本
        //创建表单sheet
        Sheet sheet = wb.createSheet("test");

        //读取图片流
        FileInputStream stream = new FileInputStream("E:\\excel\\poi\\logo.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);

        /*
        // 如下设置可以让图片限制在指定的单元格内
        // 位置  后四个参数:   前两个: 图片左上角的X,Y坐标     后两个:图片右下左上角的X,Y坐标
            XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, 1, 1, 5, 8);
            anchor.setAnchorType(ClientAnchor.AnchorType.DONT_MOVE_AND_RESIZE);
        */

        //绘制图片
        Picture picture = patriarch.createPicture(anchor, index);//图片位置,图片的索引
        picture.resize();//自适应渲染图片

        //文件流
        FileOutputStream fos = new FileOutputStream("E:\\excel\\poi\\test3.xlsx");
        //写入文件
        wb.write(fos);
        fos.close();
    }
}
5.读取excel并解析
/**
 * 读取excel并解析
 *      sheet.getLastRowNum() : 最后一行的索引(从0开始数)
 *      row.getLastCellNum() : 最后一个单元格的号码(从1开始数)
 */
public class PoiTest05 {

    public static void main(String[] args) throws Exception {
        
        // 如果excel文件过大报错,可使用下面这句代码
        ZipSecureFile.setMinInflateRatio(-1.0d);
        
        //1.根据Excel文件创建工作簿
        Workbook wb = new XSSFWorkbook("E:\\excel\\poi\\demo.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();
            
            // (这里返回的最后一列不是按索引,是从1开始)
            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;
    }
}

// poi将会把单元格内的数字都会认为是double类型,所以需要转换
public static void main(String[] args) {
    
    DecimalFormat decimalFormat1 = new DecimalFormat("#");
    DecimalFormat decimalFormat2 = new DecimalFormat("#.#");
    DecimalFormat decimalFormat3 = new DecimalFormat("#.##");
    DecimalFormat decimalFormat4 = new DecimalFormat("#.###");
    
    String format1 = decimalFormat1.format(4.25); // 这里接的参数是Object类型
    String format2 = decimalFormat2.format(4.25);
    String format3 = decimalFormat3.format(4.25);
    String format4 = decimalFormat4.format(4.25);
    
    System.out.println(format1); // 4
    System.out.println(format2); // 4.2
    System.out.println(format3); // 4.25
    System.out.println(format4); // 4.25

    System.out.println(((Double)4.25).intValue()); // 4
}
图解POI

Excel学习,# 工具类,excel

4. 基于模板输出POI报表

模板如下

Excel学习,# 工具类,excel

代码如下

// 读取类路径下的hr-demo.xlsx的模板Excel文件的样式,然后读取原有的样式并在写入数据的时候应用原有的样式
@Test
public void test_template() throws IOException, InvalidFormatException {

    // 读取模板的数据样式
    ClassPathResource resource = new ClassPathResource("hr-demo.xlsx");
    XSSFWorkbook workbook = new XSSFWorkbook(resource.getFile());

    XSSFSheet sheet = workbook.getSheetAt(0);
    XSSFRow row = sheet.getRow(2);
    short defaultRowHeight = row.getHeight(); // 获取行高
    CellStyle[] cellStyles = new CellStyle[row.getLastCellNum()];
    for (int i = 0; i < cellStyles.length; i++) {
        XSSFCell cell = row.getCell(i);
        cellStyles[i] = cell.getCellStyle();
    }


    // 使用读取到的模板样式,写入数据
    // 第一步:修改标题
    XSSFRow row0 = sheet.getRow(0);
    XSSFCell cell00 = row0.getCell(0);
    cell00.setCellValue("xx年xx月xx日人事报表");

    ArrayList<User> users = new ArrayList<>();
    users.add(new User("zj", 25));
    users.add(new User("zzhua", 23));

    int startRow = 2;
    for (User user : users) {
        XSSFRow r = sheet.createRow(startRow++);
        r.setHeight(defaultRowHeight);
        XSSFCell cell = r.createCell(0);
        for (int i = 0; i < 3; i++) {
            XSSFCell iCell = r.createCell(i);
            iCell.setCellStyle(cellStyles[i]);
            if (i == 0) {
                iCell.setCellValue(startRow-1);
            } else if (i == 1) {
                iCell.setCellValue(user.getName());
            } else {
                iCell.setCellValue(user.getAge());
            }
        }
    }

    FileOutputStream fos = new FileOutputStream("D:\\Projects\\practice\\poi\\poi"
                                                +"demo\\src\\main\\resources\\test.xlsx");
    workbook.write(fos);
    fos.flush();
    fos.close();
}
5. 自定义POI导出工具类
ExcelAttribute
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ExcelAttribute {
    /** 对应的列名称 */
    String name() default "";

    /** 列序号 */
    int sort();

    /** 字段类型对应的格式 */
    String format() default "";

}
ExcelExportUtil
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

public class ExcelExportUtil<T> {

    private int rowIndex;        // 数据开始行

    private int styleIndex;      // 数据的样式开始行
    private String templatePath; // excel模板的路径
    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 {

        // 拿到workbook,并获取到第一页
        XSSFWorkbook workbook = new XSSFWorkbook(is);
        Sheet sheet = workbook.getSheetAt(0);

        // 获取样式行,并提取所有样式
        Row styleRow = sheet.getRow(styleIndex);
        CellStyle[] styles = getTemplateStyles(styleRow);
        // 获取样式行的行高
        short defaultHeight = styleRow.getHeight();

        // 从要写的数据行开始
        AtomicInteger datasAi = new AtomicInteger(rowIndex);

        // 遍历所有的数据
        for (T t : objs) {
            // 拿到一条待写入的对象,并且遍历样式就行了
            Row row = sheet.createRow(datasAi.getAndIncrement()); // 相当于后++
            // 设置行高
            row.setHeight(defaultHeight);
            for(int i=0;i<styles.length;i++) {
                Cell cell = row.createCell(i);
                // 设置提取的样式
                cell.setCellStyle(styles[i]);
                // 根据每一列的索引匹配字段(后面可以优化下,将字段放入map,然后去找字段)
                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;
    }
}
ExcelImportUtil
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


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 {
            // 获取文件,包装成WorkBook
            XSSFWorkbook workbook = new XSSFWorkbook(is);
            // 获取第一页
            Sheet sheet = workbook.getSheetAt(0);

            // 获取到最后有数据的一行的索引(从0开始)
            int rowLength = sheet.getLastRowNum();
            System.out.println(sheet.getLastRowNum());

            // rowIndex 读取数据的开始行
            for (int rowNum = rowIndex; rowNum <= sheet.getLastRowNum(); rowNum++) {
                // 获取到所在行数据
                Row row = sheet.getRow(rowNum);
                // 反射创建对象
                entity = (T) clazz.newInstance();
                // 当前行最后有数据的列数(从1开始)
                System.out.println(row.getLastCellNum());

                // cellIndex读起的列索引(因为下面用的getCell(index))
                for (int j = cellIndex; j < row.getLastCellNum(); j++) {
                    // 根据列拿到当行对应列的单元格
                    Cell cell = row.getCell(j);
                    // 遍历所有字段,找到sort与列匹配的字段,获取值,并通过反射设置给对象
                    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 "";
        }
    }
}
6. 百万数据报表
1. 概述

我们都知道Excel可以分为早期的Excel2003版本(使用POI的HSSF对象操作)和Excel2007版本(使用POI的XSSF操作),两者对百万数据的支持如下:

  • Excel 2003:
    • 在POI中使用HSSF对象时,excel 2003最多只允许存储65536条数据,一般用来处理较少的数据量。这时对于百万级别数据,Excel肯定容纳不了。
  • Excel 2007:
    • 当POI升级到XSSF对象时,它可以直接支持excel2007以上版本,因为它采用ooxml格式。这时excel可以支持1048576条数据,单个sheet表就支持近百万条数据。但实际运行时还可能存在问题,原因是执行POI报表所产生的行对象,单元格对象,字体对象,他们都不会销毁,这就导致OOM的风险。
2. JDK性能监控工具介绍

没有性能监控工具一切推论都只能停留在理论阶段,我们可以使用Java的性能监控工具来监视程序的运行情况,包括CUP,垃圾回收,内存的分配和使用情况,这让程序的运行阶段变得更加可控,也可以用来证明我们的推测。这里我们使用JDK提供的性能工具Jvisualvm来监控程序运行。

1. Jvisualvm概述

VisualVM 是Netbeans的profile子项目,已在JDK6.0 update 7 中自带,能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的对象,反向查看分配的堆栈

2. Jvisualvm的位置

Jvisualvm位于JAVA_HOME/bin目录下,直接双击就可以打开该程序。如果只是监控本地的java进程,是不需要配置参数的,直接打开就能够进行监控。首先我们需要在本地打开一个Java程序,例如我打开员工微服务进程,这时在jvisualvm界面就可以看到与IDEA相关的Java进程了

Excel学习,# 工具类,excel

3. Jvisualvm的使用

Jvisualvm使用起来比较简单,双击点击当前运行的进程即可进入到程序的监控界面

Excel学习,# 工具类,excel

  • 概述:可以看到进程的启动参数。
  • 监视:左上:cpu利用率,gc状态的监控,右上:堆利用率,永久内存区的利用率,左下:类的监控,右下:线程的监控
  • 线程:能够显示线程的名称和运行的状态,在调试多线程时必不可少,而且可以点进一个线程查看这个线程的详细运行情况
3. 百万数据导出分析

对于百万数据量的Excel导入导出,只讨论基于Excel2007的解决方法。在ApachePoi 官方提供了对操作大数据量的导入导出的工具和解决办法,操作Excel2007使用XSSF对象,可以分为三种模式:

  • 用户模式:用户模式有许多封装好的方法操作简单,但创建太多的对象,非常耗内存(之前使用的方法

  • 事件模式基于SAX方式解析XML,SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文档,一边扫描,一边解析

  • SXSSF对象:是用来生成海量excel数据文件,主要原理是借助临时存储空间生成excel

Excel学习,# 工具类,excel

这是一张Apache POI官方提供的图片,描述了基于用户模式,事件模式,以及使用SXSSF三种方式操作Excel的特性以及CUP和内存占用情况。

1.从java安装的bin目录下打开jvisualvm.exe程序

Excel学习,# 工具类,excel

使用XSSFWorkBook写入一百万条数据,查看堆内存变化

Excel学习,# 工具类,excel

将XSSFWorkBook改为SXSSFWorkBook,启动并查看临时文件夹,有数据写入到了文件中,并且是以xml格式写入的

Excel学习,# 工具类,excel

Excel学习,# 工具类,excel

Excel学习,# 工具类,excel

Excel学习,# 工具类,excel

Excel学习,# 工具类,excel

1. 思路分析

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

2. 原理分析

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

3. 代码实现

在原有代码的基础上替换之前的XSSFWorkbook,使用SXSSFWorkbook完成创建过程即可

(百万数据导出不支持模板,有待测试,因为避免创建过多的对象)

//1.构造数据
List<EmployeeReportResult> list =
    userCompanyPersonalService.findByReport(companyId,month+"%");

//2.创建工作簿
SXSSFWorkbook workbook = new SXSSFWorkbook(); // 默认100

//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(int i=0;i<10000;i++) {
    for (EmployeeReportResult report : list) {
        Row dataRow = sheet.createRow(datasAi.getAndIncrement());
        //编号
        cell = dataRow.createCell(0);
        cell.setCellValue(report.getUserId());
        //姓名
        cell = dataRow.createCell(1);
        cell.setCellValue(report.getUsername());
        //手机
        cell = dataRow.createCell(2);
        cell.setCellValue(report.getMobile());
        //最高学历
        cell = dataRow.createCell(3);
        cell.setCellValue(report.getTheHighestDegreeOfEducation());
        //国家地区
        cell = dataRow.createCell(4);
        cell.setCellValue(report.getNationalArea());
        //护照号
        cell = dataRow.createCell(5);
        cell.setCellValue(report.getPassportNo());
        //籍贯
        cell = dataRow.createCell(6);
        cell.setCellValue(report.getNativePlace());
        //生日
        cell = dataRow.createCell(7);
        cell.setCellValue(report.getBirthday());
        //属相
        cell = dataRow.createCell(8);
        cell.setCellValue(report.getZodiac());
        //入职时间
        cell = dataRow.createCell(9);
        cell.setCellValue(report.getTimeOfEntry());
        //离职类型
        cell = dataRow.createCell(10);
        cell.setCellValue(report.getTypeOfTurnover());
        //离职原因
        cell = dataRow.createCell(11);
        cell.setCellValue(report.getReasonsForLeaving());
        //离职时间
        cell = dataRow.createCell(12);
        cell.setCellValue(report.getResignationTime());
    }
}
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());
4. 对比测试

XSSFWorkbook生成百万数据报表

使用XSSFWorkbook生成Excel报表,时间较长,随着时间推移,内存占用原来越多,直至内存溢出

Excel学习,# 工具类,excel

SXSSFWorkbook生成百万数据报表

使用SXSSFWorkbook生成Excel报表,内存占用比较平缓

Excel学习,# 工具类,excel

4.百万数据报表读取

使用POI基于事件模式解析案例提供的Excel文件

1. 思路分析

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

2. 步骤分析

1、设置POI的事件模式

根据Excel获取文件流

根据文件流创建OPCPackage

创建XSSFReader对象

2、Sax解析

自定义Sheet处理器

创建Sax的XmlReader对象

设置Sheet的事件处理器

逐行读取

3.原理分析

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

读取到某行时,调用注册的事件处理器处理数据,然后再处理下一行,同样继续调用注册的事件处理器。

Excel学习,# 工具类,excel

4.代码实现

实体类

@Data
public class PoiEntity {
    private String id;
    private String breast;
    private String adipocytes;
    private String negative;
    private String staining;
    private String supportive;
}

自定义事件处理器

/**
 * 自定义的事件处理器
 *  处理每一行数据读取
 *      实现接口
 */
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;
            }
        }
    }
}

读取百万数据的excel报表

/**
 * 使用事件模型解析百万数据excel报表
 */
public class MassivePoiTest {

    public static void main(String[] args) throws Exception {
        String path = "C:\\Users\\ThinkPad\\Desktop\\ihrm\\day8\\资源\\百万数据报表\\demo.xlsx";

        //1.根据excel报表获取OPCPackage
        OPCPackage opcPackage = OPCPackage.open(path, PackageAccess.READ);
        
        //2.创建XSSFReader
        XSSFReader reader = new XSSFReader(opcPackage);
        
        //3.获取SharedStringTable对象
        SharedStringsTable table = reader.getSharedStringsTable();
        
        //4.获取styleTable对象
        StylesTable stylesTable = reader.getStylesTable();
        
        //5.创建Sax的xmlReader对象
        XMLReader xmlReader = XMLReaderFactory.createXMLReader();
        
        //6.注册事件处理器
        XSSFSheetXMLHandler xmlHandler = new XSSFSheetXMLHandler(stylesTable,table,
                                                                 new SheetHandler(),
                                                                 false);
        xmlReader.setContentHandler(xmlHandler);
        
        //7.逐行读取
        XSSFReader.SheetIterator sheetIterator = (XSSFReader.SheetIterator) reader.getSheetsData();
        
        while (sheetIterator.hasNext()) {
            InputStream stream = sheetIterator.next(); //每一个sheet的流数据
            InputSource is = new InputSource(stream);
            xmlReader.parse(is);
        }
    }
}
5.对比测试

用户模式下读取测试Excel文件直接内存溢出,测试Excel文件映射到内存中还是占用了不少内存;事件模式下可以流畅的运行

(1)使用用户模型解析

Excel学习,# 工具类,excel

(2)使用事件模型解析

Excel学习,# 工具类,excel

通过简单的分析以及运行两种模式进行比较,可以看到用户模式下使用更简单的代码实现了Excel读取,但是在读取大文件时CPU和内存都不理想;而事件模式虽然代码写起来比较繁琐,但是在读取大文件时CPU和内存更加占优势。文章来源地址https://www.toymoban.com/news/detail-795385.html

到了这里,关于Excel学习的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 在线Excel转JSON工具

    在线Excel转JSON工具 上传excel将数据转换成json格式

    2024年02月07日
    浏览(40)
  • EXCEL 强大的数据分析工具

    Microsoft Excel 是 Microsoft 办公套件的主要部分之一,也是世界上最强大的电子表格软件之一。它使用包含具有各种行和列的表格的电子表格来组织带有公式和函数的数字和数据。它允许在 excel 公式的帮助下使用算术运算轻松处理值。此外,MS Excel 具有强大的编程支持,使其从其

    2024年02月05日
    浏览(82)
  • 开源在线excel展示插件 js excel 在线插件 合并单元格 设置单元格样式 编辑工具

     源码:https://github.com/yufb12/dataexcel.git 在线预览地地址 http://www.dataexcel.cn/dataexceljs.html 1、js 版本 es6  2、绘图引擎 zrender 地址  ZRender 文档 (ecomfe.github.io)  3、 文件保存格式json  4、创建并初始化  5、文件 新建保存

    2024年02月17日
    浏览(53)
  • EasyPoi快速入门(Excel导入导出工具)

    简介: easypoi是一款Excel快速导入导出的工具,最近有所使用,结合了网上的一些用法和官方文档的介绍,在在这里总结一下最简单的esaypoi导入Excel的实现,网上很多教程都已经封装了工具类,虽然方便,但第一次接触easypoi可能看的不是清晰,现在总结一下最为直观的SpringBoot集成easypoi快

    2023年04月22日
    浏览(42)
  • 工具.国内开源电子表格-Luckysheet(在线excel)

    国内开源电子表格-Luckysheet(在线excel,功能强大) – 【剪辑之家】 1、背景 现在web技术蓬勃发展,办公应用特别是excel都搬到了线上,比较流行的有腾讯文档,金山文档,石墨文档,google doc,这些都属于企业服务。但是小型企业或者团队,如果想自己搭建一套在线表格系统呢

    2024年02月06日
    浏览(40)
  • excel爬虫相关学习1:简单的excel爬虫

    目录 1 什么是excel 爬虫 2 EXCEL爬虫 2.1 excel 爬虫的入口  2.2 需要配置的信息 2.2.1 如何获得 ua信息 2.3 获取的信息 2.3.1 获取信息的基本内容 2.3.2 获取过程 2.3.3 我们只用关注“表视图 ” 即可 2.4 EXCEL获得的爬虫数据 加载到excel里 2.5 数据到了excel表后可以加工 2.6  如何处理已经下

    2024年02月09日
    浏览(33)
  • [软件工具]pdf多区域OCR识别导出excel工具使用教程

    首先我们打开软件,界面如下: 如上图,使用非常简单,步骤如下: (1)选择工具-取模板选择一个pdf文件划定自己需要识别的区域,如果你选择第2页指定区域则软件统一识别所有pdf第2页指定区域,划定区域后需要添加区域,最后保存模板,这样下次启动软件无需再次取模

    2024年01月18日
    浏览(37)
  • 这可能是前端处理excel最好的工具了

    大家好,我是程序视点的小二哥! 今天小二哥要分享的是一个纯前端实现读取和导出excel文件的工具库: ExcelJS 功能十分简单: 读取,操作并写入电子表格数据和样式到 XLSX 和 JSON 文件。 一个 Excel 电子表格文件逆向工程项目。 在本文中,我们使用 xlsx 文件。 xlsx 是Microsoft

    2024年02月15日
    浏览(243)
  • EasyExcel入门介绍及工具类,网络下载excel

    前言:在这里分享自己第一次使用EasyExcel并且编写工具类,且在接口中支持excel文件下载的一系列流程,包含所有前后端(JS+JAVA)完整代码,可以根据自己需要自行提取,仅供参考。 一.引入EasyExcel依赖 二.自定义创建一个实体类,如下,这里使用了lombok 其中各个注解功能如

    2024年02月12日
    浏览(59)
  • 【python工具】html中表格转化为excel

    大家在实际的工作中可能会遇到这样的场景,查看某个统计的页面数据,其中一些数据是表格形式展示的,比如这是国家统计局关于人口统计的数据: 你想将表格内容下载下来根据自己的需要进行二次加工,但是页面没有提供下载功能或者需要你登陆才能下载。那么重点来了

    2024年02月16日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包