Java处理Excel文件工具包-easyexcel使用详解

傷城~ 2022-03-16 12:18 594阅读 0赞

https://github.com/alibaba/easyexcel

由于项目需要对大量Excel数据进行输入输出处理,在使用JXL,POI后发现很容易出现OOM,最后在网上找到阿里的开源项目EasyExcel能很快速的读取写入超大Excel文件。经过大量的调试优化,现通过JAVA生成104万行20列的数据并写入到Excel文件的Sheet中只需要70秒的时间。

以下为本工程代码:

如果是maven工程在pom.xml中加入以下内容:

  1. <dependency>
  2. <groupId>com.google.guava</groupId>
  3. <artifactId>guava</artifactId>
  4. <version>27.0-jre</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>com.alibaba</groupId>
  8. <artifactId>easyexcel</artifactId>
  9. <version>1.1.2-beta5</version>
  10. </dependency>

工具包封装

  1. package utils;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.InputStream;
  8. import java.io.OutputStream;
  9. import java.util.List;
  10. import java.util.Map;
  11. import java.util.Map.Entry;
  12. import com.alibaba.excel.EasyExcelFactory;
  13. import com.alibaba.excel.ExcelReader;
  14. import com.alibaba.excel.ExcelWriter;
  15. import com.alibaba.excel.context.AnalysisContext;
  16. import com.alibaba.excel.event.AnalysisEventListener;
  17. import com.alibaba.excel.metadata.BaseRowModel;
  18. import com.alibaba.excel.metadata.Sheet;
  19. import com.google.common.base.Splitter;
  20. import com.google.common.base.Strings;
  21. import com.google.common.collect.Lists;
  22. import com.google.common.collect.Maps;
  23. import org.slf4j.Logger;
  24. import org.slf4j.LoggerFactory;
  25. /**
  26. * @author zhao.yingchao
  27. * @date 2019-02-27
  28. * @since v1.0.0
  29. */
  30. public class EasyExcelUtil {
  31. private static final Logger LOGGER = LoggerFactory.getLogger(EasyExcelUtil.class);
  32. /**
  33. * 通过String类,读取工作表数据
  34. *
  35. * @param filePath 文件路径
  36. * @return 数据集
  37. */
  38. public static Map<String, List<List<String>>> readExcelByString(String filePath) {
  39. return readExcelByString(filePath, null);
  40. }
  41. /**
  42. * 通过String类,读取工作表数据
  43. *
  44. * @param filePath 文件路径
  45. * @param sheetName sheetName
  46. * @return 数据集合
  47. */
  48. public static Map<String, List<List<String>>> readExcelByString(String filePath, String sheetName) {
  49. // 创建返回信息
  50. Map<String, List<List<String>>> dataListMap;
  51. // 解析监听器
  52. StringExcelListener excelListener = new StringExcelListener();
  53. InputStream inputStream = null;
  54. try {
  55. // 创建文件流
  56. inputStream = new FileInputStream(filePath);
  57. dataListMap = readExcelByStringFromInputStream(inputStream, sheetName);
  58. } catch (Exception e) {
  59. throw new EasyExcelException("readExcelByModel from filePath failed." + e, e);
  60. } finally {
  61. // 关闭文件流
  62. try {
  63. if (null != inputStream) {
  64. inputStream.close();
  65. }
  66. } catch (IOException e) {
  67. LOGGER.error("inputStream.close failed!", e);
  68. }
  69. }
  70. return dataListMap;
  71. }
  72. /**
  73. * 通过String类,读取工作表数据
  74. *
  75. * @param inputStream 文件流
  76. * @param sheetName sheetName
  77. * @return 数据集合
  78. */
  79. public static Map<String, List<List<String>>> readExcelByStringFromInputStream(InputStream inputStream,
  80. String sheetName) {
  81. // 创建返回信息
  82. Map<String, List<List<String>>> dataListMap = Maps.newLinkedHashMap();
  83. // 解析监听器
  84. StringExcelListener excelListener = new StringExcelListener();
  85. try {
  86. // 创建文件流
  87. ExcelReader excelReader = EasyExcelFactory.getReader(inputStream, excelListener);
  88. // 得到所有工作表
  89. List<Sheet> sheets = excelReader.getSheets();
  90. // 取所有工作表数据
  91. for (Sheet sheet : sheets) {
  92. // 工作表名称
  93. String currentSheetName = sheet.getSheetName();
  94. // 没有指定工作表,或多个工作表
  95. if (Strings.isNullOrEmpty(sheetName) || Splitter.on(',').trimResults().omitEmptyStrings().splitToList(
  96. sheetName).contains(currentSheetName)) {
  97. // 读取Excel数据
  98. excelReader.read(sheet);
  99. // 返回明细数据
  100. List<List<String>> sheetDataInfo = Lists.newArrayList(excelListener.getDataList());
  101. // 将工作表数据放入工作薄
  102. dataListMap.put(currentSheetName, sheetDataInfo);
  103. // 清除缓存数据
  104. excelListener.clear();
  105. }
  106. }
  107. } catch (Exception e) {
  108. throw new EasyExcelException("readExcelByStringFromInputStream from inputStream failed." + e, e);
  109. }
  110. return dataListMap;
  111. }
  112. /**
  113. * 通过Model类,读取工作表数据
  114. *
  115. * @param filePath 文件路径
  116. * @param clazz BaseRowModel
  117. * @return 数据集合
  118. */
  119. public static Map<String, List<? extends BaseRowModel>> readExcelByModel(String filePath,
  120. Class<? extends BaseRowModel> clazz) {
  121. return readExcelByModel(filePath, null, clazz);
  122. }
  123. /**
  124. * 通过Model类,读取工作表数据
  125. *
  126. * @param file 文件
  127. * @param clazz BaseRowModel
  128. * @return 数据集合
  129. */
  130. public static Map<String, List<? extends BaseRowModel>> readExcelByModel(File file,
  131. Class<? extends BaseRowModel> clazz) {
  132. return readExcelByModel(file, null, clazz);
  133. }
  134. /**
  135. * 通过Model类,读取工作表数据
  136. *
  137. * @param filePath 文件路径
  138. * @param sheetName sheetName
  139. * @param clazz BaseRowModel
  140. * @return 数据集合
  141. */
  142. public static List<? extends BaseRowModel> readExcelByModelSheetName(String filePath, String sheetName,
  143. Class<? extends BaseRowModel> clazz) {
  144. Map<String, List<? extends BaseRowModel>> dataListMap = readExcelByModel(filePath, sheetName, clazz);
  145. return dataListMap.getOrDefault(sheetName, null);
  146. }
  147. /**
  148. * 通过Model类,读取工作表数据
  149. *
  150. * @param file 文件
  151. * @param sheetName sheetName
  152. * @param clazz BaseRowModel
  153. * @return 数据集合
  154. */
  155. public static List<? extends BaseRowModel> readExcelByModelSheetName(File file, String sheetName,
  156. Class<? extends BaseRowModel> clazz) {
  157. Map<String, List<? extends BaseRowModel>> dataListMap = readExcelByModel(file, sheetName, clazz);
  158. return dataListMap.getOrDefault(sheetName, null);
  159. }
  160. /**
  161. * 通过Model类,读取工作表数据
  162. *
  163. * @param filePath 文件路径
  164. * @param sheetName sheetName
  165. * @param clazz BaseRowModel
  166. * @return 数据集合
  167. */
  168. public static Map<String, List<? extends BaseRowModel>> readExcelByModel(String filePath, String sheetName,
  169. Class<? extends BaseRowModel> clazz) {
  170. Map<String, List<? extends BaseRowModel>> dataListMap;
  171. InputStream inputStream = null;
  172. try {
  173. // 创建文件流
  174. inputStream = new FileInputStream(filePath);
  175. dataListMap = readExcelByModelFromInputStream(inputStream, sheetName, clazz);
  176. } catch (Exception e) {
  177. throw new EasyExcelException("readExcelByModel from filePath failed." + e, e);
  178. } finally {
  179. // 关闭文件流
  180. try {
  181. if (null != inputStream) {
  182. inputStream.close();
  183. }
  184. } catch (IOException e) {
  185. LOGGER.error("inputStream.close failed!", e);
  186. }
  187. }
  188. return dataListMap;
  189. }
  190. /**
  191. * 通过Model类,读取工作表数据
  192. *
  193. * @param file 文件
  194. * @param sheetName sheetName
  195. * @param clazz BaseRowModel
  196. * @return 数据集合
  197. */
  198. public static Map<String, List<? extends BaseRowModel>> readExcelByModel(File file, String sheetName,
  199. Class<? extends BaseRowModel> clazz) {
  200. Map<String, List<? extends BaseRowModel>> dataListMap;
  201. InputStream inputStream = null;
  202. try {
  203. // 创建文件流
  204. inputStream = new FileInputStream(file);
  205. dataListMap = readExcelByModelFromInputStream(inputStream, sheetName, clazz);
  206. // 关闭文件流
  207. inputStream.close();
  208. } catch (Exception e) {
  209. throw new EasyExcelException("readExcelByModel from File failed." + e, e);
  210. } finally {
  211. // 关闭文件流
  212. try {
  213. if (null != inputStream) {
  214. inputStream.close();
  215. }
  216. } catch (IOException e) {
  217. LOGGER.error("inputStream.close failed!", e);
  218. }
  219. }
  220. return dataListMap;
  221. }
  222. /**
  223. * 通过Model类,读取工作表数据
  224. *
  225. * @param inputStream 文件流
  226. * @param sheetName sheetName
  227. * @param clazz BaseRowModel
  228. * @return 数据集合
  229. */
  230. public static Map<String, List<? extends BaseRowModel>> readExcelByModelFromInputStream(InputStream inputStream,
  231. String sheetName,
  232. Class<?
  233. extends BaseRowModel> clazz) {
  234. // 解析每行结果在listener中处理
  235. // 创建返回信息
  236. Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap();
  237. // 解析监听器
  238. ModelExcelListener excelListener = new ModelExcelListener();
  239. try {
  240. // 创建文件流
  241. ExcelReader excelReader = EasyExcelFactory.getReader(inputStream, excelListener);
  242. // 得到所有工作表
  243. List<Sheet> sheets = excelReader.getSheets();
  244. // 取所有工作表数据
  245. for (Sheet sheet : sheets) {
  246. // 工作表名称
  247. String currentSheetName = sheet.getSheetName();
  248. if (Strings.isNullOrEmpty(sheetName) || Splitter.on(',').trimResults().omitEmptyStrings().splitToList(
  249. sheetName).contains(currentSheetName)) {
  250. // 设置模板
  251. sheet.setClazz(clazz);
  252. // 读取Excel数据
  253. excelReader.read(sheet);
  254. // 返回明细数据
  255. List<? extends BaseRowModel> sheetDataInfo = Lists.newArrayList(excelListener.getDataList());
  256. // 将工作表数据放入工作薄
  257. dataListMap.put(currentSheetName, sheetDataInfo);
  258. // 清除缓存数据
  259. excelListener.clear();
  260. }
  261. }
  262. } catch (Exception e) {
  263. throw new EasyExcelException("readExcelByModel from inputStream failed." + e, e);
  264. }
  265. return dataListMap;
  266. }
  267. /**
  268. * 通过String类,将一个sheet写入到一个Excel
  269. *
  270. * @param filePath 文件路径
  271. * @param sheetName sheetName
  272. * @param dataList 数据集
  273. */
  274. public static void writeExcelByString(String filePath, String sheetName, List<List<String>> dataList) {
  275. // 创建返回信息
  276. Map<String, List<List<String>>> dataListMap = Maps.newLinkedHashMap();
  277. // 将工作表放入到Excel中
  278. dataListMap.put(sheetName, dataList);
  279. // 输出Excel数据
  280. writeExcelByString(filePath, dataListMap);
  281. }
  282. /**
  283. * 通过String类,将多个sheet写入到一个Excel
  284. *
  285. * @param filePath 文件路径
  286. * @param dataListMap 数据集
  287. */
  288. public static void writeExcelByString(String filePath, Map<String, List<List<String>>> dataListMap) {
  289. try {
  290. // 工作表编号
  291. int sheetNo = 1;
  292. // 创建文件流
  293. OutputStream out = new FileOutputStream(filePath);
  294. ExcelWriter writer = EasyExcelFactory.getWriter(out);
  295. // 循环写入每个工作表
  296. for (Entry<String, List<List<String>>> entry : dataListMap.entrySet()) {
  297. // 得到工作表名称
  298. String sheetName = entry.getKey();
  299. // 得到工作表数据
  300. List<List<String>> dataList = entry.getValue();
  301. // 设置工作表信息
  302. Sheet sheet1 = new Sheet(sheetNo++, 1, null, sheetName, null);
  303. // 设置开始行为-1
  304. sheet1.setStartRow(-1);
  305. // 设置自适应宽度
  306. sheet1.setAutoWidth(Boolean.TRUE);
  307. // 开始写数据
  308. writer.write0(dataList, sheet1);
  309. }
  310. // 清空缓存
  311. writer.finish();
  312. // 关闭文件
  313. out.close();
  314. } catch (Exception e) {
  315. throw new EasyExcelException("writeExcelByString failed." + e, e);
  316. }
  317. }
  318. /**
  319. * 通过Model类,将一个sheet写入到一个Excel
  320. *
  321. * @param filePath 文件路径
  322. * @param sheetName sheetName
  323. * @param dataList 数据集
  324. * @param clazz BaseRowModel
  325. */
  326. public static void writeExcelByModel(String filePath, String sheetName, List<? extends BaseRowModel> dataList,
  327. Class<? extends BaseRowModel> clazz) {
  328. // 创建返回信息
  329. Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap();
  330. // 将工作表放入到Excel中
  331. dataListMap.put(sheetName, dataList);
  332. // 输出Excel数据
  333. writeExcelByModel(filePath, dataListMap, clazz);
  334. }
  335. /**
  336. * 通过String类,将多个sheet写入到一个Excel
  337. *
  338. * @param filePath 文件路径
  339. * @param dataListMap 数据集
  340. * @param clazz BaseRowModel
  341. */
  342. public static void writeExcelByModel(String filePath, Map<String, List<? extends BaseRowModel>> dataListMap,
  343. Class<? extends BaseRowModel> clazz) {
  344. try {
  345. // 创建文件流
  346. OutputStream out = new FileOutputStream(filePath);
  347. // 写入文件
  348. writeIntoOutputStream(dataListMap, clazz, out);
  349. // 关闭文件
  350. out.close();
  351. } catch (Throwable e) {
  352. throw new EasyExcelException("write to file failed." + e, e);
  353. }
  354. }
  355. /**
  356. * export to byte array.
  357. *
  358. * @param sheetName sheetName
  359. * @param dataList data
  360. * @param clazz BaseRowModel
  361. * @return return a byte array with data.
  362. */
  363. public static byte[] exportByteArray(String sheetName, List<? extends BaseRowModel> dataList,
  364. Class<? extends BaseRowModel> clazz) {
  365. // 创建返回信息
  366. Map<String, List<? extends BaseRowModel>> dataListMap = Maps.newLinkedHashMap();
  367. // 将工作表放入到Excel中
  368. dataListMap.put(sheetName, dataList);
  369. ByteArrayOutputStream out = new ByteArrayOutputStream();
  370. writeIntoOutputStream(dataListMap, clazz, out);
  371. return out.toByteArray();
  372. }
  373. /**
  374. * export to byte array.
  375. *
  376. * @param dataListMap data
  377. * @param clazz BaseRowModel
  378. * @return return a byte array with data.
  379. */
  380. public static byte[] exportByteArray(Map<String, List<? extends BaseRowModel>> dataListMap,
  381. Class<? extends BaseRowModel> clazz) {
  382. ByteArrayOutputStream out = new ByteArrayOutputStream();
  383. writeIntoOutputStream(dataListMap, clazz, out);
  384. return out.toByteArray();
  385. }
  386. /**
  387. * 数据写入输出流
  388. *
  389. * @param dataListMap 数据
  390. * @param clazz BaseRowModel
  391. * @param outputStream 输出流
  392. */
  393. public static void writeIntoOutputStream(Map<String, List<? extends BaseRowModel>> dataListMap,
  394. Class<? extends BaseRowModel> clazz,
  395. OutputStream outputStream) {
  396. try {
  397. // 工作表编号
  398. int sheetNo = 1;
  399. // 创建文件流
  400. ExcelWriter writer = EasyExcelFactory.getWriter(outputStream);
  401. // 循环写入每个工作表
  402. for (Entry<String, List<? extends BaseRowModel>> entry : dataListMap.entrySet()) {
  403. // 得到工作表名称
  404. String sheetName = entry.getKey();
  405. // 得到工作表数据
  406. List<? extends BaseRowModel> dataList = entry.getValue();
  407. // 设置工作表信息
  408. Sheet sheet1 = new Sheet(sheetNo++, 1, clazz, sheetName, null);
  409. // 设置自适应宽度
  410. sheet1.setAutoWidth(Boolean.TRUE);
  411. // 开始写数据
  412. writer.write(dataList, sheet1);
  413. }
  414. // 清空缓存
  415. writer.finish();
  416. } catch (Throwable e) {
  417. throw new EasyExcelException("write to OutputStream failed." + e, e);
  418. }
  419. }
  420. /**
  421. * String类,解析监听器
  422. */
  423. private static class StringExcelListener extends AnalysisEventListener<List<String>> {
  424. /**
  425. * 自定义用于暂时存储data 可以通过实例获取该值
  426. */
  427. private List<List<String>> dataList = Lists.newArrayList();
  428. @Override
  429. public void invoke(List<String> rowInfo, AnalysisContext context) {
  430. // 数据存储到list,供批量处理,或后续自己业务逻辑处理。
  431. dataList.add(rowInfo);
  432. }
  433. @Override
  434. public void doAfterAllAnalysed(AnalysisContext context) {
  435. //解析结束销毁不用的资源
  436. }
  437. private List<List<String>> getDataList() {
  438. return dataList;
  439. }
  440. private void setDataList(List<List<String>> dataList) {
  441. this.dataList = dataList;
  442. }
  443. private void clear() {
  444. dataList.clear();
  445. }
  446. }
  447. /**
  448. * Model类,解析监听器
  449. */
  450. private static class ModelExcelListener extends AnalysisEventListener<BaseRowModel> {
  451. /**
  452. * 自定义用于暂时存储data 可以通过实例获取该值
  453. */
  454. private List<BaseRowModel> dataList = Lists.newArrayList();
  455. @Override
  456. public void invoke(BaseRowModel rowInfo, AnalysisContext context) {
  457. dataList.add(rowInfo);
  458. }
  459. /**
  460. * 解析结束销毁不用的资源
  461. *
  462. * @param context AnalysisContext
  463. */
  464. @Override
  465. public void doAfterAllAnalysed(AnalysisContext context) {
  466. //解析结束销毁不用的资源
  467. }
  468. /**
  469. * 获取
  470. *
  471. * @return 返回sheet数据
  472. */
  473. private List<? extends BaseRowModel> getDataList() {
  474. return dataList;
  475. }
  476. /**
  477. * 设置sheet数据
  478. *
  479. * @param dataList 数据
  480. */
  481. private void setDataList(List<BaseRowModel> dataList) {
  482. this.dataList = dataList;
  483. }
  484. /**
  485. * 清空数据
  486. */
  487. private void clear() {
  488. dataList.clear();
  489. }
  490. }
  491. /**
  492. * EasyExcelException
  493. */
  494. public static class EasyExcelException extends RuntimeException {
  495. private static final long serialVersionUID = -5456062088984840434L;
  496. public EasyExcelException() {
  497. super();
  498. }
  499. public EasyExcelException(String message) {
  500. super(message);
  501. }
  502. public EasyExcelException(String message, Throwable cause) {
  503. super(message, cause);
  504. }
  505. public EasyExcelException(Throwable cause) {
  506. super(cause);
  507. }
  508. }
  509. }

样例:

  1. /**
  2. * Excel模型
  3. *
  4. * @author zhao.yingchao
  5. * @date 2019年02月27日
  6. * @since v1.0.0
  7. */
  8. @Data
  9. public class Test extends BaseRowModel {
  10. @ExcelProperty(value = "姓名", index = 0)
  11. private String name;
  12. @ExcelProperty(value = "年龄", index = 1)
  13. private String age;
  14. @ExcelProperty(value = "手机号", index = 2)
  15. private String phoneNum;
  16. }

使用:

  1. /**
  2. * @author zhao.yingchao
  3. * @date 2019-02-27
  4. * @since v1.0.0
  5. */
  6. public class EasyExcelUtilTest {
  7. @Test
  8. public void test_parse_excel() {
  9. Resource resource = new ClassPathResource("file/list1W.xlsx");
  10. try {
  11. long startTime = MonitorUtil.startLogInfo("parse begin");
  12. List<Test> list = (List<Test>)EasyExcelUtil.readExcelByModelSheetName(resource.getFile(),
  13. "Sheet1",
  14. Test.class);
  15. MonitorUtil.endLogInfo(startTime, "parse end");
  16. Assert.assertEquals(list.size(), 10000);
  17. } catch (IOException e) {
  18. e.printStackTrace();
  19. }
  20. }
  21. }

使用问题请与我反馈,谢谢

发表评论

表情:
评论列表 (有 0 条评论,594人围观)

还没有评论,来说两句吧...

相关阅读