ExcelReader.java 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. package pub.vie.excel.read;
  2. import org.apache.commons.collections4.CollectionUtils;
  3. import org.apache.poi.ss.usermodel.*;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;
  6. import pub.vie.excel.common.annotation.ExcelEntity;
  7. import pub.vie.excel.common.annotation.ExcelField;
  8. import pub.vie.excel.common.utils.CommonUtils;
  9. import java.io.BufferedInputStream;
  10. import java.io.IOException;
  11. import java.io.InputStream;
  12. import java.io.UnsupportedEncodingException;
  13. import java.lang.reflect.Field;
  14. import java.net.URL;
  15. import java.net.URLDecoder;
  16. import java.nio.charset.StandardCharsets;
  17. import java.text.SimpleDateFormat;
  18. import java.util.*;
  19. import static pub.vie.excel.common.utils.CommonUtils.arrayEmpty;
  20. import static pub.vie.excel.common.utils.CommonUtils.isBlank;
  21. /**
  22. * @Descrption :
  23. * @Author: zoujie
  24. * @Date: 2020-4-13
  25. */
  26. public class ExcelReader<T> {
  27. private static Logger log = LoggerFactory.getLogger(ExcelReader.class);
  28. private static final String DEFAULT_DATE_FORMATE = "yyyy-MM-dd";
  29. public static InputStream getStreamOnClassPath(String virtualPath) {
  30. return ExcelReader.class.getClassLoader().getResourceAsStream(virtualPath);
  31. }
  32. public static String getRealPathOnClassPath(String virtualPath) {
  33. URL resource = getClassLoader().getResource(virtualPath);
  34. if (resource == null) {
  35. return null;
  36. }
  37. String realPath;
  38. try {
  39. realPath = URLDecoder.decode(resource.getPath(), StandardCharsets.UTF_8.toString());
  40. } catch (UnsupportedEncodingException e) {
  41. e.printStackTrace();
  42. return null;
  43. }
  44. return realPath;
  45. }
  46. public List<T> read(InputStream inputStream, Class<T> dataClass) {
  47. return read(inputStream, 0, 0, -1, dataClass);
  48. }
  49. public List<T> read(InputStream inputStream,int skip, Class<T> dataClass) {
  50. return read(inputStream, 0, skip, -1, dataClass);
  51. }
  52. public List<T> read(InputStream inputStream, int sheetAt, int skip, int limitRow, Class<T> dataClass) {
  53. return baseRead(dataClass,inputStream,sheetAt,skip,limitRow,false);
  54. }
  55. public List<T> read(Class<T> dataClass) {
  56. ExcelReadProperties excelReadProperties = excelReadProperties(dataClass);
  57. if (excelReadProperties == null) {
  58. throw new IllegalArgumentException(dataClass.getName() + "不含有ExcelEntity注解信息,无法解析excel");
  59. }
  60. String classPathSource = excelReadProperties.classPathSource;
  61. if (isBlank(classPathSource)) {
  62. throw new IllegalArgumentException(dataClass.getName() + "该方法ExcelEntity注解配置的classPathSource不能为空");
  63. }
  64. int sheetAt = excelReadProperties.sheetAt;
  65. int skip = excelReadProperties.skip;
  66. int limitRow = excelReadProperties.limitRow;
  67. InputStream inputStream = getStreamOnClassPath(classPathSource);
  68. return baseRead(dataClass, inputStream, sheetAt, skip, limitRow,true);
  69. }
  70. public List<T> read(Class<T> dataClass, InputStream inputStream) {
  71. ExcelReadProperties excelReadProperties = excelReadProperties(dataClass);
  72. if (excelReadProperties == null) {
  73. throw new IllegalArgumentException(dataClass.getName() + "不含有ExcelEntity注解信息,无法解析excel");
  74. }
  75. int sheetAt = excelReadProperties.sheetAt;
  76. int skip = excelReadProperties.skip;
  77. int limitRow = excelReadProperties.limitRow;
  78. return baseRead(dataClass, inputStream, sheetAt, skip, limitRow,true);
  79. }
  80. private List<T> baseRead(Class<T> dataClass, InputStream inputStream, int sheetAt, int skip, int limitRow, boolean useAnnotation) {
  81. Workbook workbook = null;
  82. List<T> resList = null;
  83. if (inputStream == null) {
  84. log.error("excel inputStream 为 null");
  85. return null;
  86. }
  87. BufferedInputStream bufferedInputStream = convertInputStream(inputStream);
  88. try {
  89. workbook = WorkbookFactory.create(bufferedInputStream);
  90. Sheet sheet = workbook.getSheetAt(sheetAt);
  91. if (sheet == null) {
  92. log.info("获取工作簿sheetAt[{}]为空");
  93. return null;
  94. }
  95. int lastRowNum = sheet.getLastRowNum();
  96. if (skip > lastRowNum) {
  97. log.warn("sheet 总条数[{}] 跳过[{}]条不合法直接返回", lastRowNum, skip);
  98. return null;
  99. }
  100. resList = new ArrayList<>();
  101. if (skip < 0) {
  102. skip = 0;
  103. }
  104. //最大条数
  105. int end;
  106. if (limitRow <= 0 || skip + limitRow > lastRowNum) {
  107. end = lastRowNum;
  108. } else {
  109. end = skip + limitRow - 1;
  110. }
  111. for (int i = skip; i <= end; i++) {
  112. Row row = sheet.getRow(i);
  113. if (row == null) {
  114. break;
  115. }
  116. if (useAnnotation) {
  117. resList.add(handleRow(dataClass, row));
  118. } else {
  119. resList.add(handleRow(row, dataClass));
  120. }
  121. }
  122. log.info("总条数[{}] 成功处理了{}条excel数据,跳过了{}条excel数据", end, resList.size(), skip);
  123. } catch (IOException e) {
  124. e.printStackTrace();
  125. } finally {
  126. CommonUtils.close(workbook, bufferedInputStream);
  127. }
  128. return resList;
  129. }
  130. private static ClassLoader getClassLoader() {
  131. return ExcelReader.class.getClassLoader();
  132. }
  133. private T handleRow(Row row, Class<T> resDataClass) {
  134. T resData;
  135. try {
  136. resData = resDataClass.newInstance();
  137. } catch (InstantiationException | IllegalAccessException e) {
  138. e.printStackTrace();
  139. return null;
  140. }
  141. short lastCellNum = row.getLastCellNum();
  142. if (lastCellNum < 0) {
  143. throw new IllegalArgumentException("数据列小于1");
  144. }
  145. Field[] declaredFields = resDataClass.getDeclaredFields();
  146. if (arrayEmpty(declaredFields)) {
  147. throw new IllegalArgumentException(resDataClass.getName() + "未定义任何属性匹配excel文件");
  148. }
  149. int limit = declaredFields.length > lastCellNum ? lastCellNum : declaredFields.length;
  150. List<Cell> cellList = getCellList(row, limit);
  151. handleRow(Arrays.asList(declaredFields), cellList, resData, limit);
  152. return resData;
  153. }
  154. private void handleRow(List<Field> fieldList, List<Cell> cellList, Object object, int limit) {
  155. if (CollectionUtils.isEmpty(cellList) || CollectionUtils.isEmpty(fieldList)) {
  156. return;
  157. }
  158. if (limit > cellList.size() || limit > fieldList.size()) {
  159. throw new IndexOutOfBoundsException("cellList.size = " + cellList.size() + "fieldList.size=" + fieldList.size() + "max index=" + limit);
  160. }
  161. for (int i = 0; i < limit; i++) {
  162. Field field = fieldList.get(i);
  163. Cell cell = cellList.get(i);
  164. try {
  165. convertAndSet(field, object, cell);
  166. } catch (IllegalAccessException e) {
  167. log.error(e.getLocalizedMessage(), e);
  168. }
  169. }
  170. log.debug("解析到内容并封装为对象:[{}]", object);
  171. }
  172. private T handleRow(Class<T> dataClass, Row row) {
  173. Field[] declaredFields = dataClass.getDeclaredFields();
  174. Map<Integer, Field> colIndexFieldMap = selectFieldsToMap(declaredFields);
  175. if (colIndexFieldMap == null) {
  176. return null;
  177. }
  178. T dataObject;
  179. try {
  180. dataObject = dataClass.newInstance();
  181. } catch (InstantiationException | IllegalAccessException e) {
  182. throw new RuntimeException(e);
  183. }
  184. short lastCellNum = row.getLastCellNum();
  185. for (Map.Entry entry : colIndexFieldMap.entrySet()) {
  186. Integer key = (Integer) entry.getKey();
  187. Field value = (Field) entry.getValue();
  188. if (key < lastCellNum) {
  189. try {
  190. convertAndSet(value, dataObject, row.getCell(key));
  191. } catch (IllegalAccessException e) {
  192. log.error(e.getLocalizedMessage(), e);
  193. }
  194. }
  195. }
  196. return dataObject;
  197. }
  198. private BufferedInputStream convertInputStream(InputStream inputStream) {
  199. if (!(inputStream instanceof BufferedInputStream)) {
  200. return new BufferedInputStream(inputStream);
  201. } else {
  202. return (BufferedInputStream) inputStream;
  203. }
  204. }
  205. private List<Cell> getCellList(Row row, int limit) {
  206. List<Cell> cellList = new ArrayList<>();
  207. for (int j = 0; j < limit; j++) {
  208. cellList.add(row.getCell(j));
  209. }
  210. return cellList;
  211. }
  212. private void convertAndSet(Field field, Object object, Cell cell) throws IllegalAccessException {
  213. CellType cellType = cell.getCellType();
  214. field.setAccessible(true);
  215. Class<?> type = field.getType();
  216. switch (cellType) {
  217. case _NONE:
  218. log.debug("row[{}] cell[{}] 单元格内容未知类型", cell.getRowIndex(), cell.getColumnIndex());
  219. break;
  220. case NUMERIC:
  221. Double numericCellValue = cell.getNumericCellValue();
  222. if (Double.TYPE.equals(type) || Double.class.equals(type)) {
  223. field.set(object, numericCellValue);
  224. } else if (Date.class.equals(type)) {
  225. field.set(object, new Date(numericCellValue.longValue()));
  226. }else if(String.class.equals(type)){
  227. field.set(object, String.valueOf(numericCellValue));
  228. }else if(Integer.TYPE.equals(type) || Integer.class.equals(type)){
  229. field.set(object, numericCellValue.intValue());
  230. }else if(Long.TYPE.equals(type) || Long.class.equals(type)){
  231. field.set(object, numericCellValue.longValue());
  232. }
  233. log.debug("row[{}] cell[{}] 单元格内容[{}]为数字", cell.getRowIndex(), cell.getColumnIndex(), cell.getNumericCellValue());
  234. break;
  235. case STRING:
  236. String stringCellValue = cell.getStringCellValue();
  237. if (String.class.equals(type)) {
  238. field.set(object, stringCellValue);
  239. } else if (Date.class.equals(type)) {
  240. SimpleDateFormat dateFormat = getDateFormat(field);
  241. try {
  242. Date parse = dateFormat.parse(stringCellValue);
  243. field.set(object, parse);
  244. } catch (Exception e) {
  245. log.error("row[{}] col[{}] 内容为非日期类字符串,不能按照日期格式转换为Date类型或ExcelField注解配置的时间格式有误,detail message:\n{}", cell.getRowIndex(), cell.getColumnIndex(), e.getLocalizedMessage());
  246. }
  247. } else if (Boolean.TYPE.equals(type) || Boolean.class.equals(type)) {
  248. try {
  249. boolean booleanCellValue = Boolean.valueOf(stringCellValue);
  250. field.set(object, booleanCellValue);
  251. } catch (Exception e) {
  252. log.error("row[{}] col[{}] 内容为非boolean类型字符串,不能转换为boolean类型,detail message:\n{}", cell.getRowIndex(), cell.getColumnIndex(), e.getLocalizedMessage());
  253. }
  254. }
  255. log.debug("row[{}] cell[{}] 单元格内容[{}]为字符串", cell.getRowIndex(), cell.getColumnIndex(), stringCellValue);
  256. break;
  257. case FORMULA:
  258. if (String.class.equals(type)) {
  259. String formulaCellValue = cell.getCellFormula();
  260. field.set(object, formulaCellValue);
  261. }
  262. log.debug("row[{}] cell[{}] 单元格内容[{}]为公式字符串", cell.getRowIndex(), cell.getColumnIndex(), cell.getCellFormula());
  263. break;
  264. case BLANK:
  265. log.debug("row[{}] cell[{}] 单元格内容为空", cell.getRowIndex(), cell.getColumnIndex());
  266. break;
  267. case BOOLEAN:
  268. if (Boolean.TYPE.equals(type) || Boolean.class.equals(type)) {
  269. boolean booleanCellValue = cell.getBooleanCellValue();
  270. field.set(object, booleanCellValue);
  271. }
  272. log.debug("row[{}] cell[{}] 单元格内容[{}]为boolean", cell.getRowIndex(), cell.getColumnIndex(), cell.getBooleanCellValue());
  273. break;
  274. case ERROR:
  275. log.debug("row[{}] cell[{}] 单元格内容错误", cell.getRowIndex(), cell.getColumnIndex());
  276. break;
  277. default:
  278. break;
  279. }
  280. }
  281. private SimpleDateFormat getDateFormat(Field field) {
  282. ExcelField[] annotationsByType = field.getAnnotationsByType(ExcelField.class);
  283. SimpleDateFormat format;
  284. if (arrayEmpty(annotationsByType)) {
  285. format = new SimpleDateFormat(DEFAULT_DATE_FORMATE);
  286. } else {
  287. format = new SimpleDateFormat(annotationsByType[0].dataFormat());
  288. }
  289. return format;
  290. }
  291. private ExcelReadProperties excelReadProperties(Class<T> tClass) {
  292. ExcelEntity[] annotationsByType = tClass.getAnnotationsByType(ExcelEntity.class);
  293. if (arrayEmpty(annotationsByType)) {
  294. return null;
  295. }
  296. ExcelEntity excelEntity = annotationsByType[0];
  297. ExcelReadProperties properties = new ExcelReadProperties();
  298. properties.classPathSource = excelEntity.classPathSource();
  299. properties.limitRow = excelEntity.limitRow();
  300. properties.sheetAt = excelEntity.sheetAt();
  301. properties.skip = excelEntity.skip();
  302. return properties;
  303. }
  304. private Map<Integer, Field> selectFieldsToMap(Field[] fields) {
  305. if (CommonUtils.arrayEmpty(fields)) {
  306. return null;
  307. }
  308. Map<Integer, Field> selectMap = new HashMap<>();
  309. for (Field field : fields) {
  310. ExcelField[] annotationsByType = field.getAnnotationsByType(ExcelField.class);
  311. if (CommonUtils.arrayEmpty(annotationsByType)) {
  312. continue;
  313. }
  314. int i = annotationsByType[0].colIndex();
  315. if (i >= 0) {
  316. selectMap.put(annotationsByType[0].colIndex(), field);
  317. }
  318. }
  319. return selectMap;
  320. }
  321. private class ExcelReadProperties {
  322. public String classPathSource;
  323. public int sheetAt;
  324. public int skip;
  325. public int limitRow;
  326. }
  327. }