写在前面的话
POI作为比较早期的Excel处理工具,其使用较为成熟且广泛。EasyExcel相较之下,则是相对较新的工具,其却有着比POI更为优越的一些特性,如更加简单的API接口和更加优秀的性能。
性能对比:在数据量较小的情况下,POI和EasyExcel的性能表现较为相似,但在数据量较大的情况下,EasyExcel要比POI具有更快的速度。这主要得益于EasyExcel内部优化的使用反射技术,避免了POI中频繁创建Cell和Row等对象时的性能问题。
API对比:在API接口使用上,EasyExcel相较于POI也更为简单直观。POI的API接口使用较为繁琐,需要使用大量的行列、坐标等信息来操作Excel表格数据,而EasyExcel则将其进行了封装和优化,使其更加易用和方便。
简单易用性对比:EasyExcel相较于POI也更具有易用性和灵活性。EasyExcel的代码量较POI更少并且更为优美,使得开发者能够更容易的上手使用和进行二次封装,同时EasyExcel还支持自定义注解的方式进行Excel表格数据和Java对象之间的映射关系,使其在应用场景上更加丰富多彩。
其他方面的对比:POI和EasyExcel也有着各自的优缺点。如在对于老版本的Excel文件的支持上,POI更胜一筹;而在对于较为复杂的Excel表格数据处理方面,POI则有着更多的功能和接口可供探索,EasyExcel的功能相对较为局限。
1.poi简单写操作
03版本,结尾是:.xls,03和07版本的主要区别是能写的数据量大小,03版本只能写65536行,07版本基本没限制。03版本对象是:HSSFWorkbook。07版本对象是:XSSFWorkbook。
/*** poi,excel03版本* @throws IOException*/@Testpublic void test03() throws IOException {String PATH = "D:\\code\\excel";//首先创建一个工作簿HSSFWorkbook work = new HSSFWorkbook();//然后创建一个工作表HSSFSheet sheet = work.createSheet("weiqinag");//其次创建一个行(1,1)HSSFRow row = sheet.createRow(0);//其次再创建一个单元格,坐标(1,1)单元格HSSFCell cell11 = row.createCell(0);cell11.setCellValue("我是第一个单元格");//坐标(1,2)单元格HSSFCell cell12 = row.createCell(1);cell12.setCellValue("我是第一行第二个单元格");//坐标(2,1)HSSFRow row1 = sheet.createRow(1);HSSFCell cell21 = row1.createCell(0);cell21.setCellValue("我是第二行一个单元格");//坐标(2,2)HSSFCell cell22 = row1.createCell(1);String string = new DateTime().toString("yyyy-MM--dd HH:mm:ss");cell22.setCellValue(string);//生成一张表(IO)流,03版本的是xls结尾FileOutputStream fileOutputStream = new FileOutputStream(PATH +"\\"+"weiqiang03.xls");work.write(fileOutputStream);//关闭流fileOutputStream.close();System.out.println("excel生成完毕");}
07版本,结尾是:.xlsx
可以看到只是对象变化了,03版本的对象是 HSSFWorkbook,07版本的对象是XSSFWorkbook,接口没有变化,这就是面向对象的好处之一。
@Testpublic void test07()throws Exception{String PATH = "D:\\code\\excel";//首先创建一个工作簿07版本XSSFWorkbook work = new XSSFWorkbook();//然后创建一个工作表Sheet sheet = work.createSheet();//其次创建一个行(1,1)Row row = sheet.createRow(0);//其次再创建一个单元格,坐标(1,1)单元格Cell cell11 = row.createCell(0);cell11.setCellValue("我是第一个单元格");//坐标(1,2)单元格Cell cell12 = row.createCell(1);cell12.setCellValue("我是第一行第二个单元格");//坐标(2,1)Row row1 = sheet.createRow(1);Cell cell21 = row1.createCell(0);cell21.setCellValue("我是第二行一个单元格");//坐标(2,2)Cell cell22 = row1.createCell(1);String string = new DateTime().toString("yyyy-MM--dd HH:mm:ss");cell22.setCellValue(string);//生成一张表(IO)流,03版本的是xlsx结尾FileOutputStream fileOutputStream = new FileOutputStream(PATH +"\\"+"weiqiang07.xlsx");work.write(fileOutputStream);//关闭流fileOutputStream.close();System.out.println("excel生成完毕");}
2.大文件写操作
对于大文件写操作来说,03版本的缺点是最多处理65536行,否则会排除异常,优点是过程中写入缓存,不操作磁盘,最后一次性写入磁盘,速度快。
07版本的缺点是写数据非常慢,非常耗内存,会发生内存溢出的异常,如100万条,优点是可以写较大的数据量,如20万条,远远大于03版本的65536行。
2.1 03版本大文件写
HSSFWorkbook对象大数据写操作,总耗时是1.3秒左右,很快。
@Testpublic void test03BigData() throws IOException {String PATH = "D:\\code\\excel";long begin = System.currentTimeMillis();HSSFWorkbook workbook = new HSSFWorkbook();Sheet sheet = workbook.createSheet();for (int rowNum = 0; rowNum <65536 ; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}}System.out.println("循环完毕,开始写");FileOutputStream fileOutputStream = new FileOutputStream(PATH+"\\"+"bigDate.xls");workbook.write(fileOutputStream);fileOutputStream.close();long end = System.currentTimeMillis();System.out.println("消耗时间:"+(double)(end - begin)/1000);}
2.2 07版本大文件写
XSSFWorkbook对象大数据写操作,总耗时达到6.5秒左右,很慢。
@Testpublic void test07BigData() throws IOException {long begin = System.currentTimeMillis();String PATH = "D:\\code\\excel";XSSFWorkbook workbook = new XSSFWorkbook();Sheet sheet = workbook.createSheet();for (int rowNum = 0; rowNum < 65536; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}}System.out.println("循环结束,开始写操作");FileOutputStream fileOutputStream = new FileOutputStream(PATH + "\\" + "bigdate07.xlsx");workbook.write(fileOutputStream);fileOutputStream.close();long end = System.currentTimeMillis();System.out.println("消耗时间:" + (double) (end - begin) / 1000);}
2.2 SXSSF大文件写操作
根据前两个比较,03版本虽然快,但是数据量有限制,07版本的数量大,但是速度慢,那有没有速度快又写入数据量大的对象呢?有,那就是SXSSF对象,他可以写非常大的数据量,如100万条以上,写数据速度快,占用内存更少。写数据100000行,用时2.5秒左右。是不是非常快,还要记得一点,利用SXSSF对象会有临时文件产生,需要清除临时文件。
@Testpublic void testBigData() throws IOException {long begin = System.currentTimeMillis();String PATH = "D:\\code\\excel";SXSSFWorkbook workbook = new SXSSFWorkbook();Sheet sheet = workbook.createSheet();for (int rowNum = 0; rowNum < 100000; rowNum++) {Row row = sheet.createRow(rowNum);for (int cellNum = 0; cellNum < 10; cellNum++) {Cell cell = row.createCell(cellNum);cell.setCellValue(cellNum);}}System.out.println("循环结束,开始写输出流写操作");FileOutputStream fileOutputStream = new FileOutputStream(PATH + "\\" + "bigdate.xlsx");workbook.write(fileOutputStream);fileOutputStream.close();//清除临时文件((SXSSFWorkbook)workbook).dispose();long end = System.currentTimeMillis();System.out.println("消耗时间:" + (double) (end - begin) / 1000);}
3.读操作
3.1 03版本excel表的读操作
读操作,首先获取文件流,然后把流放入工作簿中,其次得到这个表,再得到行和列,最后得到行列坐标的值。读取值的时候一定要注意值的类型,否则出错。
@Testpublic void testRead03() throws IOException {String PATH = "D:\\code\\excel";//获取文件流FileInputStream fileInputStream = new FileInputStream(PATH + "\\" + "weiqiang03.xls");//首先创建一个工作簿,把流放到工作簿里HSSFWorkbook work = new HSSFWorkbook(fileInputStream);//然后得到表Sheet sheet = work.getSheetAt(0);//得到行和得到列Cell cell = sheet.getRow(0).getCell(0);//得到坐标的值System.out.println(cell.getStringCellValue());}
3.2 07版本excel表读操作
只需要换对象即可
@Testpublic void testRead03() throws IOException {String PATH = "D:\\code\\excel";//获取文件流FileInputStream fileInputStream = new FileInputStream(PATH + "\\" + "weiqiang03.xls");//首先创建一个工作簿,把流放到工作簿里XSSFWorkbook work = new XSSFWorkbook(fileInputStream);//然后得到表Sheet sheet = work.getSheetAt(0);//得到行和得到列Cell cell = sheet.getRow(0).getCell(0);//得到坐标的值System.out.println(cell.getStringCellValue());}
3.3 读取不同类型的数据(难点)
/*** 把该类封装成工具类,直接调用该方法时,传递参数fileInputStream就行了* @param fileInputStream* @throws Exception*/@Testpublic void testRead(FileInputStream fileInputStream)throws Exception{String PATH = "D:\\code\\excel";//首先获取文件流,读取哪个文件
// fileInputStream = new FileInputStream(PATH + "\\" + "03.xls");//首先创建一个工作簿07版本进行读,也可以用03版本读取,但是03文件是xls,所以用03版本读取HSSFWorkbook work = new HSSFWorkbook(fileInputStream);//然后获取这工作表Sheet sheetAt = work.getSheetAt(0);//获取第一行,就是标题Row rowTitle = sheetAt.getRow(0);//获取第一行的所有标题if (rowTitle != null) {//获取第一行所有的列int cellCount = rowTitle.getPhysicalNumberOfCells();//遍历,取出每列的内容,就是具体标题for (int cellNum = 0; cellNum < cellCount; cellNum++) {//获取列Cell cell = rowTitle.getCell(cellNum);if (cell != null) {//获取列类型int cellType = cell.getCellType();//现在默认是是String类型,一般第一行都是字符串String stringCellValue = cell.getStringCellValue();System.out.print(stringCellValue+" | ");}}System.out.println();}//获取表中的内容,先获取所有的行int rows = sheetAt.getPhysicalNumberOfRows();//rowNum =1 是从第二行开始,第一行是标题for (int rowNum = 1; rowNum < rows; rowNum++) {//获取每行的数据Row rowData = sheetAt.getRow(rowNum);//读取列,每行的每列if (rowData != null) {int cells = rowTitle.getPhysicalNumberOfCells();for (int cellsNum = 0; cellsNum < cells; cellsNum++) {System.out.print("["+(rowNum+1)+"-"+(cellsNum+1)+"]");//拿到每行每列的数据Cell cell = rowData.getCell(cellsNum);//匹配列的数据类型if (cell != null) {CellType cellType = cell.getCellTypeEnum();String cellValue= "";switch (cellType) {case STRING:System.out.print("String类型");cellValue = cell.getStringCellValue();break;case BOOLEAN:System.out.print("布尔类型");cellValue = String.valueOf(cell.getBooleanCellValue());break;case BLANK:System.out.print("空格");break;case NUMERIC:System.out.print("数字类型");//需要区分是数字还是日期if (HSSFDateUtil.isCellDateFormatted(cell)) {System.out.print("日期");Date date = cell.getDateCellValue();cellValue = new DateTime(date).toString("yyyy-MM--dd");} else {//不是日期格式,防止数字过长System.out.print("转换为字符串输出");cell.setCellType(CellType.STRING);cellValue = cell.toString();}break;case ERROR:System.out.print("数据类型错误");break;}System.out.println(cellValue);}}}}fileInputStream.close();}
4.EasyExcel读写操作
学习完poi操作Excel后,感觉是有一些困难,不太方便,所以继续学习阿里巴巴开源下的EasyExcel工具,该工具就是简单高效,会发现代码很简洁。
4.1准备一个Student类
@Data
//@ContentRowHeight(20)//内容行高
//@HeadRowHeight//表头行高
public class Student {@ExcelProperty(value = "ID",index = 4)//表头顺序@ExcelIgnoreprivate Integer sno;@ExcelProperty("姓名")private String sname;@ExcelProperty("年龄")@ColumnWidth(20)private Integer sage;@ExcelProperty("性别")private String ssex;@DateTimeFormat("yyyy-MM-dd")private Date birthday;
}
4.2 EasyExcel读操作
public class StudentListener extends AnalysisEventListener<Student> {/*** 读监听器,每读一行内容,都会调用一次该对象的invoke,在invoke中可以操作使用读取到的数据。* @param student 每次读取到的数据封装对象* @param analysisContext*/@Overridepublic void invoke(Student student, AnalysisContext analysisContext) {System.out.println("student = "+student);}/*** 读取完整的文档之后调用的方法* @param analysisContext*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}
}
/*** 读操作,从excel表格读操作,也就是导入操作。*/@Testpublic void testRead(){/*构建一个工作簿对象pathName 要读的文件的路径head 文件中每一行的数据redaListener 读监听器*///获得一个工作簿对象ExcelReaderBuilder read = EasyExcel.read("student.xlsx", Student.class, new StudentListener());//获得一个工作表的对象ExcelReaderSheetBuilder sheet = read.sheet();//读取工作表中的内容sheet.doRead();}
4.3 EasyExcel写操作
/*** 写操作,向excel文件中写内容,也就是导出操作*/@Testpublic void testWrite() {//首先是获取一个工作簿,和poi一样ExcelWriterBuilder writeWorkBook = EasyExcel.write("student-write.xls", Student.class);//然后是获取一个工作表ExcelWriterSheetBuilder sheet = writeWorkBook.sheet();//最后是写入到excel表中List<Student> students = intData();System.out.println(students);sheet.doWrite(students);}public static List<Student> intData(){ArrayList<Student> students = new ArrayList<>();for (int i = 0; i < 10; i++) {Student student = new Student();student.setSname("哈哈"+i);student.setSno(i);student.setSage(20+i);student.setSsex("男");students.add(student);}System.out.println(students);return students;}
完!