前言
最近工作中遇到需要同时支持excel和csv文件导入数据需求,而且数据量最大可达到40w条。
2020.09.18更新
发现通过easyexcel导出生成excel文件,再次通过easyexcel读取时无法得到excel的总行数,解决方法请参照
使用easyexcel读取easyexcel生成的excel文件,无法获取总行数
1 2 3 4 5 6 7 8 9 10 11 12
| ExcelWriter excelWriter =EasyExcel.write(out).registerWriteHandler(new AbstractWorkbookWriteHandler() { @Override public void afterWorkbookDispose(WriteWorkbookHolder writeWorkbookHolder) { super.afterWorkbookDispose(writeWorkbookHolder); Sheet sheet = writeWorkbookHolder.getWorkbook().getSheetAt(0); if(sheet instanceof SXSSFSheet){ SXSSFSheet sh = ((SXSSFSheet) sheet); XSSFSheet _sh = (XSSFSheet) ReflectUtil.getFieldValue(sh,"_sh"); _sh.getCTWorksheet().getDimension().setRef("A1:" + CellReference.convertNumToColString(atomicMaxColSize.get()) + atomicRowSize.get()); } } }).build();
|
SXSSFSheet只是XSSFSheet的封装类,内部还是通过XSSFSheet对象操作excel,但并没有提供get方法获取_sh属性,因此通过反射获取即可。
很奇怪,这种问题竟然一直没有解决,看来easyexcel命不久矣。
遇到的问题以及选择
数据量问题
csv
csv方面,由于项目中引用了hutool工具,加上作者本身也是参考fastcsv编写,能很好的支持读取大数据量的csv文件。
如果你项目没有引用hutool工具,使用fastcsv即可。
excel
在java中处理excel文件最常用的当然非poi莫属了,但poi并没有很好的解决大数据量oom问题,而且poi提供的api较为基础,需要自行封装,无法开箱即用。
因此我选择基于poi封装的easyexcel操作excel文件。
同时支持csv和excel问题
解决问题 csv转换为excel
那么现在需要解决的问题就是如何将csv转换为excel文件,本着不重复造轮子的原则,我搜遍了互联网,没有找到能直接用的代码。
索性自己撸一个,借助easyexcel和hutool(fastcsv)工具,写转换代码非常容易,感谢开源社区!
在本机测试下,60w数据转换需要约1分钟。
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| import cn.hutool.core.io.FileUtil; import cn.hutool.core.io.IoUtil; import cn.hutool.core.text.csv.CsvReader; import cn.hutool.core.text.csv.CsvRow; import cn.hutool.core.text.csv.CsvRowHandler; import cn.hutool.core.text.csv.CsvUtil; import cn.hutool.core.util.CharsetUtil; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.support.ExcelTypeEnum; import com.google.common.collect.Lists; import com.spepc.railwaypension.common.utils.Clock; import lombok.extern.slf4j.Slf4j;
import java.io.FileOutputStream; import java.io.OutputStream; import java.util.Arrays; import java.util.List; import java.util.concurrent.atomic.AtomicInteger;
@Slf4j public class CvsToExcelUtil { private CvsToExcelUtil() { }
public static void convert(String src, String des) throws Exception { Clock clock = new Clock().start(); CsvReader reader = CsvUtil.getReader(); int batchSize = 1000; OutputStream out = null; ExcelWriter excelWriter = null; AtomicInteger atomicInteger = new AtomicInteger(); try { out = new FileOutputStream(des); excelWriter = new ExcelWriter(out, ExcelTypeEnum.XLSX, false); Sheet sheet1 = new Sheet(1, 0); sheet1.setSheetName("sheet1"); List<List<String>> dataList = Lists.newArrayListWithCapacity(batchSize); ExcelWriter finalExcelWriter = excelWriter; reader.read(FileUtil.getReader(src, CharsetUtil.CHARSET_GBK), new CsvRowHandler() { @Override public void handle(CsvRow csvRow) { dataList.add(Arrays.asList(csvRow.toArray(new String[csvRow.size()]))); if(dataList.size()>=batchSize){ finalExcelWriter.write0(dataList, sheet1); dataList.clear(); atomicInteger.set(atomicInteger.get()+batchSize); } } }); atomicInteger.set(atomicInteger.get()+dataList.size()); excelWriter.write0(dataList, sheet1); out.flush(); } catch (Exception e) { e.printStackTrace(); throw e; } finally { if(excelWriter!=null){ excelWriter.finish(); } IoUtil.close(out); } log.info("读取并转换数据:{}条",atomicInteger.get()); log.info("转换总耗时{}秒", clock.stop().getSeconds()); }
public static void main(String[] args) throws Exception { convert("F:/test.csv","F:/test.xlsx"); } }
|
至此结束,压力山大!