ExcelUtils.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. package com.shkpr.service.alambizplugin.commtools;
  2. import com.global.base.log.LogLevelFlag;
  3. import com.global.base.log.LogPrintMgr;
  4. import com.shkpr.service.alambizplugin.annotation.ExcelMapping;
  5. import com.shkpr.service.alambizplugin.constants.ExcelEnum;
  6. import com.shkpr.service.alambizplugin.constants.LogFlagBusiType;
  7. import org.apache.commons.lang3.StringUtils;
  8. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  9. import org.apache.poi.ss.usermodel.*;
  10. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  11. import java.io.IOException;
  12. import java.io.InputStream;
  13. import java.io.OutputStream;
  14. import java.lang.reflect.Field;
  15. import java.lang.reflect.InvocationTargetException;
  16. import java.nio.file.Files;
  17. import java.nio.file.Paths;
  18. import java.util.ArrayList;
  19. import java.util.Arrays;
  20. import java.util.Collection;
  21. import java.util.HashMap;
  22. import java.util.List;
  23. import java.util.Map;
  24. import java.util.Objects;
  25. import java.util.function.Function;
  26. import java.util.stream.Collectors;
  27. import java.util.stream.IntStream;
  28. /**
  29. * excel工具类
  30. *
  31. * @author 欧阳劲驰
  32. * @serial 1.0.0
  33. */
  34. public class ExcelUtils {
  35. /**
  36. * log
  37. */
  38. private static final String mStrClassName = "ExcelUtils";
  39. private static final String mBizType = LogFlagBusiType.BUSI_GIS_SURVEY.toStrValue();
  40. /**
  41. * 解析excel文件
  42. *
  43. * @param path 文件地址
  44. * @param headerRowNum 表头行
  45. * @param dataRowNum 数据行
  46. * @return 数据(key : 页名, v : 数据)
  47. */
  48. public static Map<String, List<Map<String, String>>> parseExcelFile(String path, Integer headerRowNum, Integer dataRowNum) throws InterruptedException {
  49. //excel枚举
  50. ExcelEnum excelEnum = null;
  51. //文件后缀
  52. String ext = path.substring(path.lastIndexOf("."));
  53. //根据后缀名指定枚举
  54. if (".xls".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
  55. else if (".xlsx".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
  56. if (excelEnum == null) return null;
  57. //解析文件
  58. try {
  59. return parseExcelFile(Files.newInputStream(Paths.get(path)), excelEnum, headerRowNum, dataRowNum);
  60. } catch (IOException e) {
  61. LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
  62. , String.format("excel文件解释失败 msg:%s", e.getMessage())
  63. );
  64. return null;
  65. }
  66. }
  67. /**
  68. * 解析excel文件
  69. *
  70. * @param inputStream 输入流
  71. * @param excelEnum excel类型
  72. * @param headerRowNum 表头行
  73. * @param dataRowNum 数据行
  74. * @return 数据(key : 页名, v : 数据)
  75. */
  76. public static Map<String, List<Map<String, String>>> parseExcelFile(InputStream inputStream, ExcelEnum excelEnum
  77. , Integer headerRowNum, Integer dataRowNum) throws InterruptedException {
  78. if (inputStream == null || excelEnum == null || excelEnum == ExcelEnum.CSV) return null;
  79. //读取输入流
  80. try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new XSSFWorkbook(inputStream) : new HSSFWorkbook(inputStream)) {
  81. if (workbook.getNumberOfSheets() <= 0) return null;
  82. //结果
  83. Map<String, List<Map<String, String>>> results = new HashMap<>(workbook.getNumberOfSheets());
  84. //遍历页,并解析
  85. for (Sheet sheet : workbook) results.put(sheet.getSheetName(), parseSheet(sheet, headerRowNum, dataRowNum));
  86. return results;
  87. } catch (IOException e) {
  88. LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
  89. , String.format("excel文件解释失败 msg:%s", e.getMessage())
  90. );
  91. return null;
  92. }
  93. }
  94. /**
  95. * 解析页
  96. *
  97. * @param sheet 页
  98. * @param headerRowNum 表头行
  99. * @param dataRowNum 数据行
  100. * @return 数据
  101. */
  102. private static List<Map<String, String>> parseSheet(Sheet sheet, Integer headerRowNum, Integer dataRowNum) throws InterruptedException {
  103. //数据集合
  104. List<Map<String, String>> dataList = new ArrayList<>();
  105. if (sheet == null) return dataList;
  106. //行数
  107. int rowsNum = sheet.getPhysicalNumberOfRows();
  108. if (rowsNum <= 1) return dataList;
  109. //页头行
  110. Row headerRow = sheet.getRow(Math.max(headerRowNum - 1, 0));
  111. //字段映射
  112. Map<Integer, String> fieldMap = new HashMap<>();
  113. for (Cell cell : headerRow) {
  114. //检查线程中断,并响应
  115. if (Thread.interrupted()) throw new InterruptedException();
  116. //获取值,如不为空,则存入索引关系
  117. String cellValue = getCellValue(cell);
  118. if (!StringUtils.isEmpty(cellValue)) {
  119. fieldMap.put(cell.getColumnIndex(), cellValue);
  120. }
  121. }
  122. //遍历行
  123. for (Row row : sheet) {
  124. //检查线程中断,并响应
  125. if (Thread.interrupted()) throw new InterruptedException();
  126. //跳过非数据行
  127. if (row == null || row.getRowNum() < (dataRowNum - 1)) continue;
  128. //跳过空行
  129. if (IntStream.range(row.getFirstCellNum(), row.getLastCellNum())
  130. .mapToObj(row::getCell)
  131. .allMatch(cell -> cell == null || cell.getCellTypeEnum() == CellType.BLANK)) continue;
  132. //数据
  133. Map<String, String> data = new HashMap<>();
  134. //遍历字段映射
  135. for (Map.Entry<Integer, String> entry : fieldMap.entrySet()) {
  136. //获取单元格
  137. Cell cell = row.getCell(entry.getKey());
  138. //设置值
  139. data.put(entry.getValue(), getCellValue(cell));
  140. }
  141. //添加数据
  142. dataList.add(data);
  143. }
  144. return dataList;
  145. }
  146. /**
  147. * excel文件转数据集合
  148. *
  149. * @param path 文件地址
  150. * @param clazz 数据类型
  151. * @param headerRowNum 表头行
  152. * @param dataRowNum 数据行
  153. * @param <E> 数据泛形
  154. * @return 数据集合
  155. */
  156. public static <E> Map<String, List<E>> parseExcelFile(String path, Class<E> clazz, Integer headerRowNum, Integer dataRowNum) {
  157. //excel枚举
  158. ExcelEnum excelEnum = null;
  159. //文件后缀
  160. String ext = path.substring(path.lastIndexOf("."));
  161. //根据后缀名指定枚举
  162. if (".xls".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
  163. else if (".xlsx".equalsIgnoreCase(ext)) excelEnum = ExcelEnum.XLS;
  164. if (excelEnum == null) return null;
  165. //解析文件
  166. try {
  167. return parseExcelFile(Files.newInputStream(Paths.get(path)), clazz, excelEnum, headerRowNum, dataRowNum);
  168. } catch (IOException e) {
  169. LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
  170. , String.format("excel文件解释失败 msg:%s", e.getMessage())
  171. );
  172. return null;
  173. }
  174. }
  175. /**
  176. * excel文件转数据集合
  177. *
  178. * @param inputStream input流
  179. * @param clazz 数据类型
  180. * @param excelEnum excel枚举
  181. * @param headerRowNum 表头行
  182. * @param dataRowNum 数据行
  183. * @param <E> 数据泛形
  184. * @return 数据集合
  185. */
  186. public static <E> Map<String, List<E>> parseExcelFile(InputStream inputStream, Class<E> clazz, ExcelEnum excelEnum
  187. , Integer headerRowNum, Integer dataRowNum) {
  188. if (inputStream == null || excelEnum == null || excelEnum == ExcelEnum.CSV) return null;
  189. //读取表
  190. try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new XSSFWorkbook(inputStream) : new HSSFWorkbook(inputStream)) {
  191. if (workbook.getNumberOfSheets() <= 0) return null;
  192. //结果
  193. Map<String, List<E>> results = new HashMap<>(workbook.getNumberOfSheets());
  194. //遍历页,并解析
  195. for (Sheet sheet : workbook)
  196. results.put(sheet.getSheetName(), parseSheet(sheet, clazz, headerRowNum, dataRowNum));
  197. return results;
  198. } catch (IOException e) {
  199. LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
  200. , String.format("excel文件解释失败 msg:%s", e.getMessage())
  201. );
  202. return null;
  203. }
  204. }
  205. /**
  206. * 解析页
  207. *
  208. * @param sheet 页
  209. * @param clazz 类型
  210. * @param headerRowNum 表头行
  211. * @param dataRowNum 数据行
  212. * @param <E> 数据泛形
  213. * @return 数据
  214. */
  215. private static <E> List<E> parseSheet(Sheet sheet, Class<E> clazz, Integer headerRowNum, Integer dataRowNum) {
  216. try {
  217. //数据集合
  218. List<E> dataList = new ArrayList<>();
  219. if (sheet == null) return dataList;
  220. //行数
  221. int rowsNum = sheet.getPhysicalNumberOfRows();
  222. if (rowsNum <= 1) return dataList;
  223. //页头行
  224. Row headerRow = sheet.getRow(Math.max(headerRowNum - 1, 0));
  225. //字段映射
  226. Map<Integer, Field> fieldMap = Arrays.stream(clazz.getDeclaredFields())
  227. //过滤需要导出的字段
  228. .filter(f -> f.isAnnotationPresent(ExcelMapping.class))
  229. //设置字段公开
  230. .peek(f -> f.setAccessible(true))
  231. .collect(Collectors.toMap(
  232. f -> {
  233. //获取excel映射值
  234. String excelMappingValue = f.getAnnotation(ExcelMapping.class).value();
  235. //获取对应的索引
  236. return IntStream.range(0, headerRow.getLastCellNum())
  237. //过滤相同的值
  238. .filter(index -> headerRow.getCell(index).getStringCellValue().equals(excelMappingValue))
  239. .findFirst().orElse(-1);
  240. }, Function.identity()
  241. ));
  242. //遍历行
  243. for (Row row : sheet) {
  244. //跳过非数据
  245. if (row == null || row.getRowNum() < (dataRowNum - 1)) continue;
  246. //跳过空行
  247. if (IntStream.range(row.getFirstCellNum(), row.getLastCellNum())
  248. .mapToObj(row::getCell)
  249. .allMatch(cell -> cell == null || cell.getCellTypeEnum() == CellType.BLANK)) continue;
  250. //实列化数据
  251. E data = clazz.getDeclaredConstructor().newInstance();
  252. //遍历字段映射
  253. for (Map.Entry<Integer, Field> fieldEntry : fieldMap.entrySet()) {
  254. //跳过未找到的字段
  255. if (fieldEntry.getKey() == -1) continue;
  256. //获取单元格
  257. Cell cell = row.getCell(fieldEntry.getKey());
  258. //设置值
  259. setObjValue(data, fieldEntry.getValue(), getCellValue(cell));
  260. }
  261. //添加数据
  262. dataList.add(data);
  263. }
  264. return dataList;
  265. } catch (NoSuchMethodException | InvocationTargetException | InstantiationException |
  266. IllegalAccessException e) {
  267. LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
  268. , String.format("excel页解释失败 msg:%s", e.getMessage())
  269. );
  270. return null;
  271. }
  272. }
  273. /**
  274. * 写入文件
  275. *
  276. * @param headers 表头信息
  277. * @param datas 数据
  278. * @param outputStream 输出流
  279. * @param excelEnum excel枚举
  280. * @param headerRowNum 表头行
  281. * @param dataRowNum 数据行
  282. */
  283. public static void writeFile(Map<String, Map<String, String>> headers, Map<String, List<Map<String, Object>>> datas
  284. , OutputStream outputStream, ExcelEnum excelEnum, Integer headerRowNum, Integer dataRowNum) {
  285. //创建表
  286. try (Workbook workbook = excelEnum == ExcelEnum.XLSX ? new XSSFWorkbook() : new HSSFWorkbook()) {
  287. //表头样式
  288. CellStyle headerStyle = workbook.createCellStyle();
  289. headerStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
  290. headerStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
  291. Font headerFont = workbook.createFont();
  292. headerFont.setBold(true);
  293. headerStyle.setFont(headerFont);
  294. //遍历页
  295. for (Map.Entry<String, Map<String, String>> headersEntry : headers.entrySet()) {
  296. //创建页
  297. Sheet sheet = workbook.createSheet(headersEntry.getKey());
  298. if (!datas.containsKey(headersEntry.getKey())) continue;
  299. //写入页
  300. writeSheet(headersEntry.getValue(), datas.get(headersEntry.getKey()), sheet, headerStyle, headerRowNum, dataRowNum);
  301. }
  302. //写入文件
  303. workbook.write(outputStream);
  304. } catch (IOException e) {
  305. LogPrintMgr.getInstance().printLogMsg(LogLevelFlag.LOG_ERROR, mBizType, mStrClassName
  306. , String.format("excel文件写入失败 msg:%s", e.getMessage())
  307. );
  308. }
  309. }
  310. /**
  311. * 写入页
  312. *
  313. * @param header 表头
  314. * @param data 数据
  315. * @param sheet 页
  316. * @param headerStyle 头样式
  317. * @param headerRowNum 表头行
  318. * @param dataRowNum 数据行
  319. */
  320. public static void writeSheet(Map<String, String> header, List<Map<String, Object>> data
  321. , Sheet sheet, CellStyle headerStyle, Integer headerRowNum, Integer dataRowNum) {
  322. //表头键
  323. List<String> headerKeys = new ArrayList<>();
  324. //表头行
  325. Row headRow = sheet.createRow(Math.max(headerRowNum - 1, 0));
  326. //遍历表头
  327. for (Map.Entry<String, String> headerEntry : header.entrySet()) {
  328. //缓存键
  329. headerKeys.add(headerEntry.getKey());
  330. //设置值和样式
  331. Cell cell = headRow.createCell(headerKeys.size() - 1);
  332. setCellValue(cell, headerEntry.getValue());
  333. cell.setCellStyle(headerStyle);
  334. }
  335. //遍历数据
  336. for (int i = 0; i < data.size(); i++) {
  337. Map<String, Object> item = data.get(i);
  338. Row row = sheet.createRow(i + dataRowNum - 1);
  339. //遍历表头键
  340. for (int j = 0, headerKeysSize = headerKeys.size(); j < headerKeysSize; j++) {
  341. //根据表头键,设置值
  342. String headerKey = headerKeys.get(j);
  343. Cell cell = row.createCell(j);
  344. setCellValue(cell, item.get(headerKey));
  345. }
  346. }
  347. //设置自动宽度
  348. for (int i = 0; i < header.size(); i++) {
  349. sheet.autoSizeColumn(i, true);
  350. sheet.setColumnWidth(i, sheet.getColumnWidth(i) * 12 / 10);
  351. }
  352. }
  353. /**
  354. * 获取单元格值
  355. *
  356. * @param cell 单元格
  357. * @return 值
  358. */
  359. private static String getCellValue(Cell cell) {
  360. if (cell == null) return null;
  361. //值
  362. String value = null;
  363. try {
  364. switch (cell.getCellTypeEnum()) {
  365. case NUMERIC: {
  366. if (DateUtil.isCellDateFormatted(cell)) {
  367. value = TimeTool.convertDateObj2DateStr(cell.getDateCellValue(), cell.getCellStyle().getDataFormatString());
  368. } else if (String.valueOf(cell.getNumericCellValue()).contains(".")) {
  369. value = String.format("%f", cell.getNumericCellValue());
  370. } else
  371. value = String.valueOf(cell.getNumericCellValue());
  372. }
  373. break;
  374. case STRING:
  375. value = cell.getRichStringCellValue().toString();
  376. break;
  377. case BOOLEAN:
  378. value = String.valueOf(cell.getBooleanCellValue());
  379. break;
  380. case FORMULA:
  381. value = String.valueOf(cell.getNumericCellValue());
  382. break;
  383. default:
  384. break;
  385. }
  386. } catch (Exception ignored) {
  387. }
  388. return value == null ? "" : value.trim();
  389. }
  390. /**
  391. * 设置单元格值
  392. *
  393. * @param cell 单元格
  394. * @param value 值
  395. */
  396. private static void setCellValue(Cell cell, Object value) {
  397. if (value == null) cell.setCellValue("");
  398. else if (value instanceof String) cell.setCellValue((String) value);
  399. else if (value instanceof Integer) cell.setCellValue((Integer) value);
  400. else if (value instanceof Double) cell.setCellValue((Double) value);
  401. else if (value instanceof Boolean) cell.setCellValue((Boolean) value);
  402. else if (value instanceof Long) cell.setCellValue((Long) value);
  403. else if (value instanceof Float) cell.setCellValue((Float) value);
  404. else if (value instanceof Short) cell.setCellValue((Short) value);
  405. else if (value instanceof Byte) cell.setCellValue((Byte) value);
  406. else if (value instanceof Character) cell.setCellValue(value.toString());
  407. else if (value instanceof Collection<?>)
  408. cell.setCellValue(((Collection<?>) value).stream().map(Objects::toString).collect(Collectors.joining(",")));
  409. else cell.setCellValue(value.toString());
  410. }
  411. /**
  412. * 设置数据值
  413. *
  414. * @param data 数据
  415. * @param field 字段
  416. * @param value 值
  417. * @param <E> 数据泛形
  418. * @throws IllegalAccessException 非法访问异常
  419. */
  420. private static <E> void setObjValue(E data, Field field, Object value) throws IllegalAccessException {
  421. //值判断空
  422. if (value != null) {
  423. //判断数据类型是否相同,如相同直接设置值,如不同,则转换值
  424. if (field.getType().isAssignableFrom(value.getClass())) {
  425. //设置值
  426. field.set(data, value);
  427. } else {
  428. //如值为double类型
  429. if (value instanceof Double) {
  430. //对double类型兼容的类型处理
  431. if (field.getType() == short.class || field.getType() == Short.class)
  432. field.set(data, ((Double) value).shortValue());
  433. else if (field.getType() == int.class || field.getType() == Integer.class)
  434. field.set(data, ((Double) value).intValue());
  435. else if (field.getType() == long.class || field.getType() == Long.class)
  436. field.set(data, ((Double) value).longValue());
  437. else if (field.getType() == float.class || field.getType() == Float.class)
  438. field.set(data, ((Double) value).floatValue());
  439. }
  440. }
  441. }
  442. }
  443. }