Excel学习
文章目录
- 学习链接
- 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的应用场景
- 数据报表生成
- 数据备份
- 数据批量上传
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和内存更加占优势。
相关文章:

Excel学习
文章目录 学习链接Excel1. Excel的两种形式2. 常见excel操作工具3.POI1. POI的概述2. POI的应用场景3. 使用1.使用POI创建excel2.创建单元格写入内容3.单元格样式处理4.插入图片5.读取excel并解析图解POI 4. 基于模板输出POI报表5. 自定义POI导出工具类ExcelAttributeExcelExpo…...
【技能---labelme软件的安装及其使用--ubuntu】
文章目录 概要Labelme 是什么?Labelme 能干啥? Ubuntu20.04安装Labelme1.Anaconda的安装2.Labelme的安装3.Labelme的使用 概要 图像检测需要自己的数据集,为此需要对一些数据进行数据标注,这里提供了一种图像的常用标注工具——la…...

回归预测 | Matlab实现SSA-CNN-LSTM-Attention麻雀优化卷积长短期记忆神经网络注意力机制多变量回归预测(SE注意力机制)
回归预测 | Matlab实现SSA-CNN-LSTM-Attention麻雀优化卷积长短期记忆神经网络注意力机制多变量回归预测(SE注意力机制) 目录 回归预测 | Matlab实现SSA-CNN-LSTM-Attention麻雀优化卷积长短期记忆神经网络注意力机制多变量回归预测(SE注意力…...
css垂直水平居中的几种实现方式
垂直水平居中的几种实现方式 一、固定宽高: 1、定位 margin-top margin-left .box-container{position: relative;width: 300px;height: 300px;}.box-container .box {width: 200px; height: 100px;position: absolute; left: 50%; top: 50%;margin-top: -50px;…...

OpenHarmony之hdc
OpenHarmony之hdc 简介 hdc(OpenHarmony Device Connector)是 OpenHarmony 为开发人员提供的用于调试的命令行工具,通过该工具可以在Windows/Linux/MacOS等系统上与开发机或者模拟器进行交互。 类似于Android的adb,和adb类似&a…...

【爬虫实战】-爬取微博之夜盛典评论,爬取了1.7w条数据
前言: TaoTao之前在前几期推文中发布了一个篇weibo评论的爬虫。主要就是采集评论区的数据,包括评论、评论者ip、评论id、评论者等一些信息。然后有很多的小伙伴对这个代码很感兴趣。TaoTao也都给代码开源了。由于比较匆忙,所以没来得及去讲这…...

CST2024的License服务成功启动,仍报错——“The desired daemon is down...”,适用于任何版本!基础设置遗漏!
CST2024的License服务成功启动,仍报错——“The desired daemon is down…”,适用于任何版本!基础设置遗漏! CST2024的License服务成功启动后报错 若不能成功启动License服务,有可能是你的计算机名称带中文ÿ…...

matlab中any()函数用法
一、帮助文档中的介绍 B any(A) 沿着大小不等于 1 的数组 A 的第一维测试所有元素为非零数字还是逻辑值 1 (true)。实际上,any 是逻辑 OR 运算符的原生扩展。 二、解读 分两步走: ①确定维度;②确定运算规则 以下面二维数组为例 >>…...

Apache ECharts | 一个数据可视化图表库
文章目录 1、简介1.1、主要特点1.2、使用场景 2、安装方式一:从下载的源代码或编译产物安装方法二:从 npm 安装方法三:⭐定制安装echarts.js 3、使用 官网: 英语:https://echarts.apache.org/en/index.html 中文&a…...

m1 + swoole(hyperf) + yasd + phpstorm 安装和debug
参考文档 Mac M1安装报错 checking for boost... configure: error: lib boost not found. Try: install boost library Issue #89 swoole/yasd GitHub 1.安装boost库 brew install boostbrew link boost 2.下载yasd git clone https://github.com/swoole/yasd.git 3.编…...

group by 查询慢的话,如何优化?
1、说明 根据一定的规则,进行分组。 group by可能会慢在哪里?因为它既用到临时表,又默认用到排序。有时候还可能用到磁盘临时表。 如果执行过程中,会发现内存临时表大小到达了上限(控制这个上限的参数就是tmp_table…...

【重学C语言】一、C语言简介
【重学C语言】一、C语言简介 什么是编程语言?编程语言 C语言发展史C语言标准变迁开发软件CLion安装步骤 VIsual Studio安装步骤 Clion 和 VS2022 绑定 电脑常识 什么是编程语言? 人类语言:语言就是人类进行沟通交流的表达方式,应…...

【MATLAB源码-第109期】基于matlab的哈里斯鹰优化算发(HHO)机器人栅格路径规划,输出做短路径图和适应度曲线。
操作环境: MATLAB 2022a 1、算法描述 哈里斯鹰优化算法(Harris Hawk Optimization, HHO)是一种受自然界捕食行为启发的优化算法。它基于哈里斯鹰的捕猎策略和行为模式,主要用于解决各种复杂的优化问题。这个算法的核心特征在于…...
NestJS 如何自定义中间件以及实际项目基于中间件提升项目开发效率
前言 NestJS 作为一个强大的 Node.js 框架,允许你通过中间件对请求和响应进行处理。中间件的概念在其他许多框架中也存在,它们在请求处理流程的早期执行,因此非常适合执行如日志记录、请求验证、设置响应头等任务。 在这篇教程中࿰…...

CMake入门教程【核心篇】设置和使用缓存变量
😈「CSDN主页」:传送门 😈「Bilibil首页」:传送门 😈「动动你的小手」:点赞👍收藏⭐️评论📝 文章目录 概述设置缓存变量使用缓存变量更改缓存变量完整代码示例实战使用技巧注意事项总结与分析...
MinIO (五) .net core实现分片上传
开发环境 Win11 vs2022 appsettings.json添加配置项 //minIO配置"MinIO": {//服务器IP"Endpoint": "192.168.xx.xx:9090",//账号"AccessKey": "3xR7i4zs1vLnxxxxxxxx",//密码"SecretKey": "P6bAnyzJm47Ub…...

如何有效提高矢量网络分析仪的动态范围
动态范围是网络分析仪(VNA)接收机的最大输入功率与最小可测量功率(本底噪声)之间的差值,如图所示,要使测量有效,输入信号必须在这些边界内。 如果需要测量信号幅度非常大的变化,例如…...

Python 安卓开发:Kivy、BeeWare、Flet、Flutter
kivy:https://github.com/kivy python-for-android :https://python-for-android.readthedocs.io/en/latest/ BeeWare:https://docs.beeware.org/en/latest/ Flet:https://github.com/flet-dev/flet 把 PySide6 移植到安卓上去&a…...
50天精通Golang(第16天)
beego框架介绍和流程分析 beego官方文档:https://beego.me/ 一、beego框架介绍 1.1 beego框架介绍–beego简介 1.1.1 什么是beego beego是一个使用Go语言来开发WEB引用的GoWeb框架,该框架起始于2012年,由一位中国的程序员编写并进行公开…...

imx6ull基于yocto工程的l汇编点亮ed
通过汇编点亮led 在裸机状态下通过汇编点亮led,即没有操作系统,(uboot kernel rootfs 都不需要实现)。 led点亮原理 1.GPIO复用 根据原理图,找到led对应的引脚(pin),复用为GPIO(只有GPIO才能…...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)
引言:为什么 Eureka 依然是存量系统的核心? 尽管 Nacos 等新注册中心崛起,但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制,是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

九天毕昇深度学习平台 | 如何安装库?
pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子: 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...
代码随想录刷题day30
1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币,另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额,返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

Unity UGUI Button事件流程
场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

nnUNet V2修改网络——暴力替换网络为UNet++
更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...