当前位置:   article > 正文

java解析大数据量xlsx格式excel_streamingreader

streamingreader

百度了好久关于解析excel的内容都找不到自己想要的东西,所以希望跟我有一样需求的人,能够因为这篇文章少走弯路.
  
  excel有两种格式,一种xls格式(97),一种xlsx格式(07). 提到excel API可能首先想到的是POI,使用POI能够读写所有的excel,但是POI针对于每种格式的excel分了好几种模式,UserModel EventModel UserEventModel

 本人使用usermodel解析excel,10M大小的文件,就会OOM,因为usermodel模式是一次性加载所有的内容到内存,每个Cell就是一个对象,导致内存被撑爆.对于内存要求比较高或者说是文件比较大(10M也算大?)的场景,应该使用EventModel或者UserEventModel的API(我没研究,官网给的东西比较难看).这两种API比较复杂.
  
  因为本人的需求是解析xlsx格式的excel,针对于读取/解析excel的需求,可不必依赖POI , Streaming-Reader是对于POI的再次封装,并且简单易懂,虽然只能针对于xlsx格式的excel读取/解析(xls跟xlsx底层本质是不一样的),但是针对于这种需求,可以完美的解决掉爆内存的问题.
  
  解决内存问题的关键就是使用流式处理,读取一批数据解析完后就释放,再进行下一批,Streaming-Reader就是使用这种方式.
 

  1. <dependency>
  2. <groupId>com.monitorjbl</groupId>
  3. <artifactId>xlsx-streamer</artifactId>
  4. <version>2.0.0</version>
  5. </dependency>

下面是示例代码

  1. import com.monitorjbl.xlsx.StreamingReader;
  2. import org.apache.poi.ss.usermodel.*;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. public class Test {
  6. public static void main(String[] args) throws Exception {
  7. FileInputStream in = new FileInputStream(new File("src/190917_MEAC Aug Database - v1.xlsx"));
  8. Workbook open = StreamingReader.builder()
  9. .rowCacheSize(100)//一次读取多少行(默认是10行)
  10. .bufferSize(1024)//使用的缓冲大小(默认1024)
  11. .open(in);
  12. for (Sheet sheet : open) {
  13. for (Row row : sheet) {
  14. for (Cell cell : row) {
  15. }
  16. }
  17. }
  18. }
  19. }

基本逻辑就是这样子,是不是很简单? 可以根据sheet的名字取出特定sheet,但是无法指定具体的rownumber来获取行,因为这是基于流的方式读取excel内容,流的单位是.rowCacheSize(100)指定的行数,我们不可能在当前100行的流中,去指定第101行的Row.至于其他的API 可以自行翻看源码,源码中的内容通俗易懂.

下面贴出来的是我在解析中的一个实例.
 

  1. public class DataImport {
  2. public static void main(String[] args) throws FileNotFoundException, SQLException {
  3. DataImportJdbc dataImportJdbc = new DataImportJdbc();
  4. HashMap<String, String> excDBMapping = dataImportJdbc.readMapping("smme");
  5. Map<String, Integer> fieldsIndexMapping = new HashMap<String, Integer>();
  6. FileInputStream in = new FileInputStream(new File("src/190917_MEAC Aug Database - v1.xlsx"));
  7. Workbook open = StreamingReader.builder()
  8. .rowCacheSize(100)
  9. .bufferSize(1024)
  10. .open(in);
  11. HashMap<String, Integer> monthMapping = SMMEFields.monthMapping;
  12. HashMap<String, Integer> realMonthMapping = new HashMap<>();
  13. StreamingSheet sheet = (StreamingSheet) open.getSheet("Raw");
  14. for (Row row : sheet) {
  15. if (row.getRowNum() == 0) {
  16. for (Cell cell : row) {
  17. String stringCellValue = cell.getStringCellValue().toLowerCase();
  18. if (excDBMapping.containsKey(stringCellValue)) {
  19. fieldsIndexMapping.put(excDBMapping.get(stringCellValue), cell.getColumnIndex());
  20. } else if (monthMapping.containsKey(stringCellValue)) {
  21. if("year".equals(stringCellValue)) {
  22. realMonthMapping.put(stringCellValue, cell.getColumnIndex());
  23. }else{
  24. String month = MonthMatch.SMMEMonthMatch(stringCellValue);
  25. realMonthMapping.put(month, cell.getColumnIndex());
  26. }
  27. }
  28. }
  29. System.out.println(realMonthMapping);
  30. } else {
  31. Integer countryIndex = fieldsIndexMapping.get("country");
  32. String country = row.getCell(countryIndex).getStringCellValue();
  33. if (SMMEFields.countryList.contains(country)) {
  34. System.out.println(country);
  35. continue;
  36. }
  37. HashMap<String, String> stringStringHashMap = new HashMap<String, String>();
  38. for (String key : fieldsIndexMapping.keySet()) {
  39. Cell cell = row.getCell(fieldsIndexMapping.get(key));
  40. String stringCellValue = cell.getStringCellValue();
  41. stringStringHashMap.put(key, stringCellValue);
  42. }
  43. for (String key : realMonthMapping.keySet()) {
  44. if (!"year".equals(key) && realMonthMapping.get(key) != null) {
  45. Integer integer = realMonthMapping.get(key);
  46. Cell cell = row.getCell(integer);
  47. String volume = cell.getStringCellValue();
  48. if (volume.trim().isEmpty() || volume.length() == 0 || volume.equals("- 0")) {
  49. volume = "0";
  50. } else if (volume.contains(",")) {
  51. volume = volume.replace(",", "");
  52. }
  53. String year = row.getCell(realMonthMapping.get("year")).getStringCellValue();
  54. String yearMonth = year + key;
  55. String time = DateUtils.getStringTime();
  56. OneData oneData = new OneData();
  57. oneData.setMap(stringStringHashMap);
  58. oneData.setTime(time);
  59. oneData.setYearmonth(yearMonth);
  60. oneData.setVolume(volume);
  61. String id = DigestUtils.md5DigestAsHex(oneData.toString().getBytes());
  62. oneData.setId(id);
  63. dataImportJdbc.upsertData(oneData);
  64. }
  65. }
  66. }
  67. }
  68. }
  69. }

不用揣想上面的代码中我要做什么,只需要看基本的逻辑,以及API的使用就好.

如果使用中出现异常

  1. Exception in thread "main" java.lang.UnsupportedOperationException
  2. at com.monitorjbl.xlsx.impl.StreamingSheet.getFirstRowNum(StreamingSheet.java:118)
  3. at com.parsh.Test.main(Test.java:21)

那是因为源码方法中没有逻辑内容,直接通过Throw来抛出了这个异常.看到这个异常就是说明,有这个方法,但是功能没实现,请不要使用这个方法的意思.

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号