Excel学习

news/2024/2/25 13:04:58/文章来源:https://blog.csdn.net/qq_16992475/article/details/135562828

文章目录

    • 学习链接
    • Excel
      • 1. Excel的两种形式
      • 2. 常见excel操作工具
      • 3.POI
        • 1. POI的概述
        • 2. POI的应用场景
        • 3. 使用
          • 1.使用POI创建excel
          • 2.创建单元格写入内容
          • 3.单元格样式处理
          • 4.插入图片
          • 5.读取excel并解析
          • ==图解POI==
        • 4. 基于模板输出POI报表
        • 5. 自定义POI导出工具类
          • ExcelAttribute
          • ExcelExportUtil
          • ExcelImportUtil
        • 6. 百万数据报表
          • 1. 概述
          • 2. JDK性能监控工具介绍
            • 1. Jvisualvm概述
            • 2. Jvisualvm的位置
            • 3. Jvisualvm的使用
          • 3. 百万数据导出分析
            • 1. 思路分析
            • 2. 原理分析
            • 3. 代码实现
            • 4. 对比测试
          • 4.百万数据报表读取
            • 1. 思路分析
            • 2. 步骤分析
            • 3.原理分析
            • 4.代码实现
            • 5.对比测试

学习链接

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

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

Excel文件导入导出 - 专栏

【狂神说Java】POI技术详解

Excel

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

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

1. Excel的两种形式

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

在这里插入图片描述

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 -- 2003Workbook wb = new XSSFWorkbook(); //2007版本//2.创建表单sheetSheet 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 -- 2003Workbook wb = new XSSFWorkbook(); //2007版本//创建表单sheetSheet 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 -- 2003Workbook wb = new XSSFWorkbook(); //2007版本//创建表单sheetSheet 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 -- 2003Workbook wb = new XSSFWorkbook(); //2007版本//创建表单sheetSheet 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.获取SheetSheet 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); // 4System.out.println(format2); // 4.2System.out.println(format3); // 4.25System.out.println(format4); // 4.25System.out.println(((Double)4.25).intValue()); // 4
}
图解POI

在这里插入图片描述

4. 基于模板输出POI报表

模板如下

在这里插入图片描述

代码如下

// 读取类路径下的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 {// 获取文件,包装成WorkBookXSSFWorkbook 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();}// 去除 浮点型 自动加的 .0if (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进程了

在这里插入图片描述

3. Jvisualvm的使用

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

在这里插入图片描述

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

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

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

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

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

在这里插入图片描述

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

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

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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=" + newString(fileName.getBytes("ISO8859-1")));
response.setHeader("filename", fileName);
workbook.write(response.getOutputStream());
4. 对比测试

XSSFWorkbook生成百万数据报表

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

在这里插入图片描述

SXSSFWorkbook生成百万数据报表

使用SXSSFWorkbook生成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文档中读取数据的机制。它逐行扫描文档,一边扫描一边解析。由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中,这对于大型文档的解析是个巨大优势

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

在这里插入图片描述

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:行索引*/@Overridepublic void startRow(int i) {//实例化对象if(i>0) {entity = new PoiEntity();}}/*** 当结束解析某一行的时候触发*      i:行索引*/@Overridepublic void endRow(int i) {//使用对象进行业务操作System.out.println(entity);}/*** 对行中的每一个表格进行处理*      cellReference: 单元格名称*      value:数据*      xssfComment:批注*/@Overridepublic 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报表获取OPCPackageOPCPackage opcPackage = OPCPackage.open(path, PackageAccess.READ);//2.创建XSSFReaderXSSFReader 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)使用用户模型解析

在这里插入图片描述

(2)使用事件模型解析

在这里插入图片描述

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_925539.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Spring Boot】项目端口号冲突解决方法,一步到位

启动项目遇到以下问题&#xff1a; Description: Web server failed to start. Port 8080 was already in use. Action: Identify and stop the process that’s listening on port 8080 or configure this application to listen on another port. Process finished with …

python学习笔记10(选择结构2、循环结构1)

&#xff08;一&#xff09;选择结构2 1、if……else……语句 #&#xff08;1&#xff09;基本格式 numbereval(input("请输入您的6位中奖号码&#xff1a;")) if number123456:print("恭喜您&#xff0c;中奖了") else:print("未中奖")#&…

jupyter notebook 配置conda 虚拟环境python

conda创建python环境 conda create -n openvoice python3.9 激活环境 source activate openvoice 在虚拟环境中安装ipykernel pip install ipykernel 添加虚拟环境进到 jupyter notebook python -m ipykernel install --user --name openvoice --display-name openvoice …

C#,入门教程(15)——类(class)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(14)——字符串与其他数据类型的转换https://blog.csdn.net/beijinghorn/article/details/124004562 物以类聚&#xff0c;凡物必类。 类的使用&#xff0c;须遵循几个简单的原则&#xff1a; &#xff08;1&#xff09;能类则类&a…

数字智慧驱动:数据可视化如何助力大企业效率飙升?

在当今信息大爆炸的时代&#xff0c;大型企业面临着前所未有的数据挑战。数据量庞大、多样化的信息汇聚&#xff0c;无疑成为企业高效运营的挑战之一。幸运的是&#xff0c;数据可视化作为一种强大的工具&#xff0c;正成为大型企业提高效率、优化决策的得力助手。 数据可视化首…

IP定位技术在网络安全行业的探索

随着互联网的普及和深入生活&#xff0c;网络安全问题日益受到人们的关注。作为网络安全领域的重要技术&#xff0c;IP定位技术正逐渐成为行业研究的热点。本文将深入探讨IP定位技术在网络安全行业的应用和探索。 一、IP定位技术的概述 IP定位技术是通过IP地址来确定设备地理位…

【Linux】第二十八站:动静态库

文章目录 一、设计静态库1.自己设计一个静态库2.使用一下我们的静态库3.动态链接和静态链接4.得到的一些结论5.可以不用写-I和-L的两种方法 二、设计动态库1.自己设计一个动态库2.使用一下我们的动态库3.得到的结论 三、动态库是怎么被加载的 一、设计静态库 在我们之前提到过…

电脑扩容升级硬盘选1T还是2T

SSD固态有必要升级2TB吗&#xff1f;----------吴中函 某大二学生用的一台笔记本电脑&#xff0c;512GB的硬盘空间已经严重不够用了&#xff0c;想给笔记本扩容升级一下硬盘&#xff1b; 这位学生是学设计专业的、平时也喜欢摄影、电脑里面也装了一些游戏&#xff0c;经常整理、…

C# wpf 实现任意控件(包括窗口)更多调整大小功能

WPF拖动改变大小系列 第一节 Grid内控件拖动调整大小 第二节 Canvas内控件拖动调整大小 第三节 窗口拖动调整大小 第四节 附加属性实现拖动调整大小 第五章 拓展更多调整大小功能&#xff08;本章&#xff09; 文章目录 WPF拖动改变大小系列前言一、添加的功能1、任意控件Drag…

SpringBoot参数校验

介绍 在开发现代应用程序时&#xff0c;数据验证是确保用户输入的正确性和应用程序数据完整性的关键方面。Spring Boot 提供了强大的数据验证机制&#xff0c;使开发者能够轻松地执行验证操作。本文将深入介绍 Spring Boot 中的 Validation&#xff0c;以及如何在应用程序中正…

网络文件共享服务

目录 一、网络文件共享服务原理内容 1.存储类型 2.应用场景 3.总结 二、FTP——文件传输协议 1.工作原理介绍 2.vsftpd软件 2.1使用ftp 2.2延伸——FileZilla 2.3修改默认端口号 2.4主动模式端口 2.5被动模式端口 2.6匿名用户登录 2.7匿名用户上传 2.8匿名用户…

5文件操作

包含头文件<fstream> 操作文件三大类&#xff1a; ofstream : 写文件ifstream &#xff1a;读文件fstream : 读写文件 5.1文本文件 -文件以ascii的形式存储在计算机中 5.1.1写文件 步骤&#xff1a; 包含头文件 #include "fstream"创建流对象 ofs…

Open CASCADE学习|参数化球面的奇异性

参数曲面的奇异性是一个相对复杂的概念&#xff0c;它涉及到参数曲面的几何特性和参数化过程中的一些特殊情况。参数曲面通常用于描述三维空间中的复杂形状&#xff0c;通过参数方程将二维参数域映射到三维空间中。然而&#xff0c;在某些情况下&#xff0c;参数曲面可能会表现…

ssm基于vue的儿童教育网站的设计与实现论文

摘 要 传统信息的管理大部分依赖于管理人员的手工登记与管理&#xff0c;然而&#xff0c;随着近些年信息技术的迅猛发展&#xff0c;让许多比较老套的信息管理模式进行了更新迭代&#xff0c;视频信息因为其管理内容繁杂&#xff0c;管理数量繁多导致手工进行处理不能满足广大…

react 项目结构配置

1 项目整体目录结构的搭建 如下图&#xff1a; 2 重置css样式: normalize.css reset.less ; 第一步 安装 npm i normalize.css 入口文件index.tsx导入&#xff1a;import ‘noremalize.css’ 第二步 创建自己的css样式&#xff1a;在assets文件夹中创建css…

Go 知多少?

作为一名已接触过其他语言的开发&#xff0c;再去学习一门新语言可比之前轻松不少&#xff0c; 语言之间存在很多相似点&#xff0c;但是新语言也有自己的不同点&#xff0c;通常我会先了解它与其他语言常遇到的不同点有哪些&#xff0c; 使自己先能够上手编写基础程序&#…

深入理解 Spark(一)spark 运行模式简介与启动流程源码分析

spark 的运行模式 standalone 模式 以 standalone-client 为例&#xff0c;运行过程如下&#xff1a; SparkContext 连接到 Master&#xff0c;向 Master 注册并申请资源&#xff08;CPU Core 和 Memory&#xff09;&#xff1b;Master 根据 SparkContext 的资源申请要求和 …

ruoyi后台管理系统部署-4-安装nginx

yum 安装 ngix 1.24 yum 官方源安装&#xff1a; # 1. 需要预先安装 yum-utils sudo yum install yum-utils # 2. 配置yum repo touch /etc/yum.repos.d/nginx.repongix.repo: [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$…

ETF交易好不好?如何选择一个好的ETF基金?

作为普通投资者&#xff0c;交易ETF基金时&#xff0c;拥有证券账户即可&#xff0c;如同买卖股票一样操作&#xff0c;非常便捷&#xff0c;可以在盘中实时买卖。和投资股票相比较&#xff0c;风险相对较低。 首先&#xff0c;什么是ETF基金? ETF其实是一个缩写&#xff1a…

41k+ stars 闪电般快速的开源搜索引擎 docker安装教程

目录 1.下载 2.启动 成功示例 3.创建索引 4.插入数据 4.1下载数据 4.2插入数据 4.3查看数据 5.官方地址 1.下载 docker pull getmeili/meilisearch:latest 2.启动 mkdir -p /opt/meili_datadocker run -it --rm \-p 7700:7700 \-v /opt/meili_data:/meili_data \ge…