ExcelUtils2.java 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. package com.template.common.utils;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.template.annotation.ExcelExport;
  5. import com.template.annotation.ExcelImport;
  6. import com.template.controller.SmartFaceDiscernController;
  7. import org.apache.poi.hssf.usermodel.HSSFDataValidation;
  8. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  9. import org.apache.poi.poifs.filesystem.POIFSFileSystem;
  10. import org.apache.poi.ss.usermodel.*;
  11. import org.apache.poi.ss.usermodel.ClientAnchor.AnchorType;
  12. import org.apache.poi.ss.util.CellRangeAddress;
  13. import org.apache.poi.ss.util.CellRangeAddressList;
  14. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  15. import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
  16. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  17. import org.slf4j.Logger;
  18. import org.slf4j.LoggerFactory;
  19. import org.springframework.web.multipart.MultipartFile;
  20. import javax.servlet.ServletOutputStream;
  21. import javax.servlet.http.HttpServletResponse;
  22. import java.io.*;
  23. import java.lang.reflect.Field;
  24. import java.math.BigDecimal;
  25. import java.math.RoundingMode;
  26. import java.net.URL;
  27. import java.text.NumberFormat;
  28. import java.text.SimpleDateFormat;
  29. import java.util.*;
  30. import java.util.Map.Entry;
  31. import java.util.regex.Pattern;
  32. /**
  33. * Excel导入导出工具类
  34. *
  35. *
  36. * @author sunnyzyq
  37. * @date 2021/12/17
  38. */
  39. @SuppressWarnings("unused")
  40. public class ExcelUtils2 {
  41. private static Logger logger = LoggerFactory.getLogger(ExcelUtils2.class);
  42. private static final String XLSX = ".xlsx";
  43. private static final String XLS = ".xls";
  44. public static final String ROW_MERGE = "row_merge";
  45. public static final String COLUMN_MERGE = "column_merge";
  46. private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
  47. private static final String ROW_NUM = "rowNum";
  48. private static final String ROW_DATA = "rowData";
  49. private static final String ROW_TIPS = "rowTips";
  50. private static final int CELL_OTHER = 0;
  51. private static final int CELL_ROW_MERGE = 1;
  52. private static final int CELL_COLUMN_MERGE = 2;
  53. private static final int IMG_HEIGHT = 30;
  54. private static final int IMG_WIDTH = 30;
  55. private static final char LEAN_LINE = '/';
  56. private static final int BYTES_DEFAULT_LENGTH = 10240;
  57. private static final NumberFormat NUMBER_FORMAT = NumberFormat.getNumberInstance();
  58. public static <T> List<T> readFile(File file, Class<T> clazz) throws Exception {
  59. JSONArray array = readFile(file);
  60. return getBeanList(array, clazz);
  61. }
  62. public static <T> List<T> readMultipartFile(MultipartFile mFile, Class<T> clazz) throws Exception {
  63. JSONArray array = readMultipartFile(mFile);
  64. return getBeanList(array, clazz);
  65. }
  66. public static JSONArray readFile(File file) throws Exception {
  67. return readExcel(null, file);
  68. }
  69. public static JSONArray readMultipartFile(MultipartFile mFile) throws Exception {
  70. return readExcel(mFile, null);
  71. }
  72. public static Map<String, JSONArray> readFileManySheet(File file) throws Exception {
  73. return readExcelManySheet(null, file);
  74. }
  75. public static Map<String, JSONArray> readFileManySheet(MultipartFile file) throws Exception {
  76. return readExcelManySheet(file, null);
  77. }
  78. private static <T> List<T> getBeanList(JSONArray array, Class<T> clazz) throws Exception {
  79. List<T> list = new ArrayList<>();
  80. Map<Integer, String> uniqueMap = new HashMap<>(16);
  81. for (int i = 0; i < array.size(); i++) {
  82. list.add(getBean(clazz, array.getJSONObject(i), uniqueMap));
  83. }
  84. return list;
  85. }
  86. /**
  87. * 获取每个对象的数据
  88. */
  89. private static <T> T getBean(Class<T> c, JSONObject obj, Map<Integer, String> uniqueMap) throws Exception {
  90. T t = c.newInstance();
  91. Field[] fields = c.getDeclaredFields();
  92. List<String> errMsgList = new ArrayList<>();
  93. boolean hasRowTipsField = false;
  94. StringBuilder uniqueBuilder = new StringBuilder();
  95. int rowNum = 0;
  96. for (Field field : fields) {
  97. // 行号
  98. if (field.getName().equals(ROW_NUM)) {
  99. rowNum = obj.getInteger(ROW_NUM);
  100. field.setAccessible(true);
  101. field.set(t, rowNum);
  102. continue;
  103. }
  104. // 是否需要设置异常信息
  105. if (field.getName().equals(ROW_TIPS)) {
  106. hasRowTipsField = true;
  107. continue;
  108. }
  109. // 原始数据
  110. if (field.getName().equals(ROW_DATA)) {
  111. field.setAccessible(true);
  112. field.set(t, obj.toString());
  113. continue;
  114. }
  115. // 设置对应属性值
  116. setFieldValue(t, field, obj, uniqueBuilder, errMsgList);
  117. }
  118. // 数据唯一性校验
  119. if (uniqueBuilder.length() > 0) {
  120. if (uniqueMap.containsValue(uniqueBuilder.toString())) {
  121. Set<Integer> rowNumKeys = uniqueMap.keySet();
  122. for (Integer num : rowNumKeys) {
  123. if (uniqueMap.get(num).equals(uniqueBuilder.toString())) {
  124. errMsgList.add(String.format("数据唯一性校验失败,(%s)与第%s行重复)", uniqueBuilder, num));
  125. }
  126. }
  127. } else {
  128. uniqueMap.put(rowNum, uniqueBuilder.toString());
  129. }
  130. }
  131. // 失败处理
  132. if (errMsgList.isEmpty() && !hasRowTipsField) {
  133. return t;
  134. }
  135. StringBuilder sb = new StringBuilder();
  136. int size = errMsgList.size();
  137. for (int i = 0; i < size; i++) {
  138. if (i == size - 1) {
  139. sb.append(errMsgList.get(i));
  140. } else {
  141. sb.append(errMsgList.get(i)).append(";");
  142. }
  143. }
  144. // 设置错误信息
  145. for (Field field : fields) {
  146. if (field.getName().equals(ROW_TIPS)) {
  147. field.setAccessible(true);
  148. field.set(t, sb.toString());
  149. }
  150. }
  151. return t;
  152. }
  153. private static <T> void setFieldValue(T t, Field field, JSONObject obj, StringBuilder uniqueBuilder, List<String> errMsgList) {
  154. // 获取 ExcelImport 注解属性
  155. ExcelImport annotation = field.getAnnotation(ExcelImport.class);
  156. if (annotation == null) {
  157. return;
  158. }
  159. String cname = annotation.value();
  160. if (cname.trim().length() == 0) {
  161. return;
  162. }
  163. // 获取具体值
  164. String val = null;
  165. if (obj.containsKey(cname)) {
  166. val = getString(obj.getString(cname));
  167. }
  168. if (val == null) {
  169. return;
  170. }
  171. field.setAccessible(true);
  172. // 判断是否必填
  173. boolean require = annotation.required();
  174. if (require && val.isEmpty()) {
  175. errMsgList.add(String.format("[%s]不能为空", cname));
  176. return;
  177. }
  178. // 数据唯一性获取
  179. boolean unique = annotation.unique();
  180. if (unique) {
  181. if (uniqueBuilder.length() > 0) {
  182. uniqueBuilder.append("--").append(val);
  183. } else {
  184. uniqueBuilder.append(val);
  185. }
  186. }
  187. // 判断是否超过最大长度
  188. int maxLength = annotation.maxLength();
  189. if (maxLength > 0 && val.length() > maxLength) {
  190. errMsgList.add(String.format("[%s]长度不能超过%s个字符(当前%s个字符)", cname, maxLength, val.length()));
  191. }
  192. // 判断当前属性是否有映射关系
  193. LinkedHashMap<String, String> kvMap = getKvMap(annotation.kv());
  194. if (!kvMap.isEmpty()) {
  195. boolean isMatch = false;
  196. for (String key : kvMap.keySet()) {
  197. if (kvMap.get(key).equals(val)) {
  198. val = key;
  199. isMatch = true;
  200. break;
  201. }
  202. }
  203. if (!isMatch) {
  204. errMsgList.add(String.format("[%s]的值不正确(当前值为%s)", cname, val));
  205. return;
  206. }
  207. }
  208. // 其余情况根据类型赋值
  209. String fieldClassName = field.getType().getSimpleName();
  210. try {
  211. if ("String".equalsIgnoreCase(fieldClassName)) {
  212. field.set(t, val);
  213. } else if ("boolean".equalsIgnoreCase(fieldClassName)) {
  214. field.set(t, Boolean.valueOf(val));
  215. } else if ("int".equalsIgnoreCase(fieldClassName) || "Integer".equals(fieldClassName)) {
  216. try {
  217. field.set(t, Integer.valueOf(val));
  218. } catch (NumberFormatException e) {
  219. errMsgList.add(String.format("[%s]的值格式不正确(当前值为%s)", cname, val));
  220. }
  221. } else if ("double".equalsIgnoreCase(fieldClassName)) {
  222. field.set(t, Double.valueOf(val));
  223. } else if ("long".equalsIgnoreCase(fieldClassName)) {
  224. field.set(t, Long.valueOf(val));
  225. } else if ("BigDecimal".equalsIgnoreCase(fieldClassName)) {
  226. field.set(t, new BigDecimal(val));
  227. } else if ("Date".equalsIgnoreCase(fieldClassName)) {
  228. try {
  229. field.set(t, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(val));
  230. } catch (Exception e) {
  231. field.set(t, new SimpleDateFormat("yyyy-MM-dd").parse(val));
  232. }
  233. }
  234. } catch (Exception e) {
  235. e.printStackTrace();
  236. }
  237. }
  238. private static Map<String, JSONArray> readExcelManySheet(MultipartFile mFile, File file) throws IOException {
  239. Workbook book = getWorkbook(mFile, file);
  240. if (book == null) {
  241. return Collections.emptyMap();
  242. }
  243. Map<String, JSONArray> map = new LinkedHashMap<>();
  244. for (int i = 0; i < book.getNumberOfSheets(); i++) {
  245. Sheet sheet = book.getSheetAt(i);
  246. JSONArray arr = readSheet(sheet);
  247. map.put(sheet.getSheetName(), arr);
  248. }
  249. book.close();
  250. return map;
  251. }
  252. private static JSONArray readExcel(MultipartFile mFile, File file) throws IOException {
  253. Workbook book = getWorkbook(mFile, file);
  254. if (book == null) {
  255. return new JSONArray();
  256. }
  257. JSONArray array = readSheet(book.getSheetAt(0));
  258. book.close();
  259. return array;
  260. }
  261. private static Workbook getWorkbook(MultipartFile mFile, File file) throws IOException {
  262. boolean fileNotExist = (file == null || !file.exists());
  263. if (mFile == null && fileNotExist) {
  264. return null;
  265. }
  266. // 解析表格数据
  267. InputStream in;
  268. String fileName;
  269. if (mFile != null) {
  270. // 上传文件解析
  271. in = mFile.getInputStream();
  272. fileName = getString(mFile.getOriginalFilename()).toLowerCase();
  273. } else {
  274. // 本地文件解析
  275. in = new FileInputStream(file);
  276. fileName = file.getName().toLowerCase();
  277. }
  278. Workbook book;
  279. if (fileName.endsWith(XLSX)) {
  280. book = new XSSFWorkbook(in);
  281. } else if (fileName.endsWith(XLS)) {
  282. POIFSFileSystem poifsFileSystem = new POIFSFileSystem(in);
  283. book = new HSSFWorkbook(poifsFileSystem);
  284. } else {
  285. return null;
  286. }
  287. in.close();
  288. return book;
  289. }
  290. private static JSONArray readSheet(Sheet sheet) {
  291. // 首行下标
  292. int rowStart = sheet.getFirstRowNum();
  293. // 尾行下标
  294. int rowEnd = sheet.getLastRowNum();
  295. // 获取表头行
  296. Row headRow = sheet.getRow(rowStart);
  297. if (headRow == null) {
  298. return new JSONArray();
  299. }
  300. int cellStart = headRow.getFirstCellNum();
  301. int cellEnd = headRow.getLastCellNum();
  302. Map<Integer, String> keyMap = new HashMap<>();
  303. for (int j = cellStart; j < cellEnd; j++) {
  304. // 获取表头数据
  305. String val = getCellValue(headRow.getCell(j));
  306. if (val != null && val.trim().length() != 0) {
  307. keyMap.put(j, val);
  308. }
  309. }
  310. // 如果表头没有数据则不进行解析
  311. if (keyMap.isEmpty()) {
  312. return (JSONArray) Collections.emptyList();
  313. }
  314. // 获取每行JSON对象的值
  315. JSONArray array = new JSONArray();
  316. // 如果首行与尾行相同,表明只有一行,返回表头数据
  317. if (rowStart == rowEnd) {
  318. JSONObject obj = new JSONObject();
  319. // 添加行号
  320. obj.put(ROW_NUM, 1);
  321. for (int i : keyMap.keySet()) {
  322. obj.put(keyMap.get(i), "");
  323. }
  324. array.add(obj);
  325. return array;
  326. }
  327. for (int i = rowStart + 1; i <= rowEnd; i++) {
  328. Row eachRow = sheet.getRow(i);
  329. JSONObject obj = new JSONObject();
  330. // 添加行号
  331. obj.put(ROW_NUM, i + 1);
  332. StringBuilder sb = new StringBuilder();
  333. for (int k = cellStart; k < cellEnd; k++) {
  334. if (eachRow != null) {
  335. String val = getCellValue(eachRow.getCell(k));
  336. // 所有数据添加到里面,用于判断该行是否为空
  337. sb.append(val);
  338. obj.put(keyMap.get(k), val);
  339. }
  340. }
  341. if (sb.length() > 0) {
  342. array.add(obj);
  343. }
  344. }
  345. return array;
  346. }
  347. private static String getCellValue(Cell cell) {
  348. // 空白或空
  349. if (cell == null || cell.getCellTypeEnum() == CellType.BLANK) {
  350. return "";
  351. }
  352. // String类型
  353. if (cell.getCellTypeEnum() == CellType.STRING) {
  354. String val = cell.getStringCellValue();
  355. if (val == null || val.trim().length() == 0) {
  356. return "";
  357. }
  358. return val.trim();
  359. }
  360. // 数字类型
  361. if (cell.getCellTypeEnum() == CellType.NUMERIC) {
  362. String s = cell.getNumericCellValue() + "";
  363. // 去掉尾巴上的小数点0
  364. if (Pattern.matches(".*\\.0*", s)) {
  365. return s.split("\\.")[0];
  366. } else {
  367. return s;
  368. }
  369. }
  370. // 布尔值类型
  371. if (cell.getCellTypeEnum() == CellType.BOOLEAN) {
  372. return cell.getBooleanCellValue() + "";
  373. }
  374. // 错误类型
  375. return cell.getCellFormula();
  376. }
  377. public static <T> void exportTemplate(HttpServletResponse response, String fileName, Class<T> clazz) {
  378. exportTemplate(response, fileName, fileName, clazz, false);
  379. }
  380. public static <T> void exportTemplate(HttpServletResponse response, String fileName, String sheetName,
  381. Class<T> clazz) {
  382. exportTemplate(response, fileName, sheetName, clazz, false);
  383. }
  384. public static <T> void exportTemplate(HttpServletResponse response, String fileName, Class<T> clazz,
  385. boolean isContainExample) {
  386. exportTemplate(response, fileName, fileName, clazz, isContainExample);
  387. }
  388. public static <T> void exportTemplate(HttpServletResponse response, String fileName, String sheetName,
  389. Class<T> clazz, boolean isContainExample) {
  390. // 获取表头字段
  391. List<ExcelClassField> headFieldList = getExcelClassFieldList(clazz);
  392. // 获取表头数据和示例数据
  393. List<List<Object>> sheetDataList = new ArrayList<>();
  394. List<Object> headList = new ArrayList<>();
  395. List<Object> exampleList = new ArrayList<>();
  396. Map<Integer, List<String>> selectMap = new LinkedHashMap<>();
  397. for (int i = 0; i < headFieldList.size(); i++) {
  398. ExcelClassField each = headFieldList.get(i);
  399. headList.add(each.getName());
  400. exampleList.add(each.getExample());
  401. LinkedHashMap<String, String> kvMap = each.getKvMap();
  402. if (kvMap != null && kvMap.size() > 0) {
  403. selectMap.put(i, new ArrayList<>(kvMap.values()));
  404. }
  405. }
  406. sheetDataList.add(headList);
  407. if (isContainExample) {
  408. sheetDataList.add(exampleList);
  409. }
  410. // 导出数据
  411. export(response, fileName, sheetName, sheetDataList, selectMap);
  412. }
  413. private static <T> List<ExcelClassField> getExcelClassFieldList(Class<T> clazz) {
  414. // 解析所有字段
  415. Field[] fields = clazz.getDeclaredFields();
  416. boolean hasExportAnnotation = false;
  417. Map<Integer, List<ExcelClassField>> map = new LinkedHashMap<>();
  418. List<Integer> sortList = new ArrayList<>();
  419. for (Field field : fields) {
  420. ExcelClassField cf = getExcelClassField(field);
  421. if (cf.getHasAnnotation() == 1) {
  422. hasExportAnnotation = true;
  423. }
  424. int sort = cf.getSort();
  425. if (map.containsKey(sort)) {
  426. map.get(sort).add(cf);
  427. } else {
  428. List<ExcelClassField> list = new ArrayList<>();
  429. list.add(cf);
  430. sortList.add(sort);
  431. map.put(sort, list);
  432. }
  433. }
  434. Collections.sort(sortList);
  435. // 获取表头
  436. List<ExcelClassField> headFieldList = new ArrayList<>();
  437. if (hasExportAnnotation) {
  438. for (Integer sort : sortList) {
  439. for (ExcelClassField cf : map.get(sort)) {
  440. if (cf.getHasAnnotation() == 1) {
  441. headFieldList.add(cf);
  442. }
  443. }
  444. }
  445. } else {
  446. headFieldList.addAll(map.get(0));
  447. }
  448. return headFieldList;
  449. }
  450. private static ExcelClassField getExcelClassField(Field field) {
  451. ExcelClassField cf = new ExcelClassField();
  452. String fieldName = field.getName();
  453. cf.setFieldName(fieldName);
  454. ExcelExport annotation = field.getAnnotation(ExcelExport.class);
  455. // 无 ExcelExport 注解情况
  456. if (annotation == null) {
  457. cf.setHasAnnotation(0);
  458. cf.setName(fieldName);
  459. cf.setSort(0);
  460. return cf;
  461. }
  462. // 有 ExcelExport 注解情况
  463. cf.setHasAnnotation(1);
  464. cf.setName(annotation.value());
  465. String example = getString(annotation.example());
  466. if (!example.isEmpty()) {
  467. if (isNumeric(example) && example.length() < 8) {
  468. cf.setExample(Double.valueOf(example));
  469. } else {
  470. cf.setExample(example);
  471. }
  472. } else {
  473. cf.setExample("");
  474. }
  475. cf.setSort(annotation.sort());
  476. // 解析映射
  477. String kv = getString(annotation.kv());
  478. cf.setKvMap(getKvMap(kv));
  479. return cf;
  480. }
  481. private static LinkedHashMap<String, String> getKvMap(String kv) {
  482. LinkedHashMap<String, String> kvMap = new LinkedHashMap<>();
  483. if (kv.isEmpty()) {
  484. return kvMap;
  485. }
  486. String[] kvs = kv.split(";");
  487. if (kvs.length == 0) {
  488. return kvMap;
  489. }
  490. for (String each : kvs) {
  491. String[] eachKv = getString(each).split("-");
  492. if (eachKv.length != 2) {
  493. continue;
  494. }
  495. String k = eachKv[0];
  496. String v = eachKv[1];
  497. if (k.isEmpty() || v.isEmpty()) {
  498. continue;
  499. }
  500. kvMap.put(k, v);
  501. }
  502. return kvMap;
  503. }
  504. /**
  505. * 导出表格到本地
  506. *
  507. * @param file 本地文件对象
  508. * @param sheetData 导出数据
  509. */
  510. public static void exportFile(File file, List<List<Object>> sheetData) {
  511. if (file == null) {
  512. logger.info("文件创建失败");
  513. return;
  514. }
  515. if (sheetData == null) {
  516. sheetData = new ArrayList<>();
  517. }
  518. Map<String, List<List<Object>>> map = new HashMap<>();
  519. map.put(file.getName(), sheetData);
  520. export(null, file, file.getName(), map, null);
  521. }
  522. /**
  523. * 导出表格到本地
  524. *
  525. * @param <T> 导出数据类似,和K类型保持一致
  526. * @param filePath 文件父路径(如:D:/doc/excel/)
  527. * @param fileName 文件名称(不带尾缀,如:学生表)
  528. * @param list 导出数据
  529. * @throws IOException IO异常
  530. */
  531. public static <T> File exportFile(String filePath, String fileName, List<T> list) throws IOException {
  532. File file = getFile(filePath, fileName);
  533. List<List<Object>> sheetData = getSheetData(list);
  534. exportFile(file, sheetData);
  535. return file;
  536. }
  537. /**
  538. * 获取文件
  539. *
  540. * @param filePath filePath 文件父路径(如:D:/doc/excel/)
  541. * @param fileName 文件名称(不带尾缀,如:用户表)
  542. * @return 本地File文件对象
  543. */
  544. private static File getFile(String filePath, String fileName) throws IOException {
  545. String dirPath = getString(filePath);
  546. String fileFullPath;
  547. if (dirPath.isEmpty()) {
  548. fileFullPath = fileName;
  549. } else {
  550. // 判定文件夹是否存在,如果不存在,则级联创建
  551. File dirFile = new File(dirPath);
  552. if (!dirFile.exists()) {
  553. boolean mkdirs = dirFile.mkdirs();
  554. if (!mkdirs) {
  555. return null;
  556. }
  557. }
  558. // 获取文件夹全名
  559. if (dirPath.endsWith(String.valueOf(LEAN_LINE))) {
  560. fileFullPath = dirPath + fileName + XLSX;
  561. } else {
  562. fileFullPath = dirPath + LEAN_LINE + fileName + XLSX;
  563. }
  564. }
  565. logger.info(fileFullPath);
  566. File file = new File(fileFullPath);
  567. if (!file.exists()) {
  568. boolean result = file.createNewFile();
  569. if (!result) {
  570. return null;
  571. }
  572. }
  573. return file;
  574. }
  575. private static <T> List<List<Object>> getSheetData(List<T> list) {
  576. // 获取表头字段
  577. List<ExcelClassField> excelClassFieldList = getExcelClassFieldList(list.get(0).getClass());
  578. List<String> headFieldList = new ArrayList<>();
  579. List<Object> headList = new ArrayList<>();
  580. Map<String, ExcelClassField> headFieldMap = new HashMap<>();
  581. for (ExcelClassField each : excelClassFieldList) {
  582. String fieldName = each.getFieldName();
  583. headFieldList.add(fieldName);
  584. headFieldMap.put(fieldName, each);
  585. headList.add(each.getName());
  586. }
  587. // 添加表头名称
  588. List<List<Object>> sheetDataList = new ArrayList<>();
  589. sheetDataList.add(headList);
  590. // 获取表数据
  591. for (T t : list) {
  592. Map<String, Object> fieldDataMap = getFieldDataMap(t);
  593. Set<String> fieldDataKeys = fieldDataMap.keySet();
  594. List<Object> rowList = new ArrayList<>();
  595. for (String headField : headFieldList) {
  596. if (!fieldDataKeys.contains(headField)) {
  597. continue;
  598. }
  599. Object data = fieldDataMap.get(headField);
  600. if (data == null) {
  601. rowList.add("");
  602. continue;
  603. }
  604. ExcelClassField cf = headFieldMap.get(headField);
  605. // 判断是否有映射关系
  606. LinkedHashMap<String, String> kvMap = cf.getKvMap();
  607. if (kvMap == null || kvMap.isEmpty()) {
  608. rowList.add(data);
  609. continue;
  610. }
  611. String val = kvMap.get(data.toString());
  612. if (isNumeric(val)) {
  613. rowList.add(Double.valueOf(val));
  614. } else {
  615. rowList.add(val);
  616. }
  617. }
  618. sheetDataList.add(rowList);
  619. }
  620. return sheetDataList;
  621. }
  622. private static <T> Map<String, Object> getFieldDataMap(T t) {
  623. Map<String, Object> map = new HashMap<>();
  624. Field[] fields = t.getClass().getDeclaredFields();
  625. try {
  626. for (Field field : fields) {
  627. String fieldName = field.getName();
  628. field.setAccessible(true);
  629. Object object = field.get(t);
  630. map.put(fieldName, object);
  631. }
  632. } catch (IllegalArgumentException | IllegalAccessException e) {
  633. e.printStackTrace();
  634. }
  635. return map;
  636. }
  637. public static void exportEmpty(HttpServletResponse response, String fileName) {
  638. List<List<Object>> sheetDataList = new ArrayList<>();
  639. List<Object> headList = new ArrayList<>();
  640. headList.add("导出无数据");
  641. sheetDataList.add(headList);
  642. export(response, fileName, sheetDataList);
  643. }
  644. public static void export(HttpServletResponse response, String fileName, List<List<Object>> sheetDataList) {
  645. export(response, fileName, fileName, sheetDataList, null);
  646. }
  647. public static void exportManySheet(HttpServletResponse response, String fileName, Map<String, List<List<Object>>> sheetMap) {
  648. export(response, null, fileName, sheetMap, null);
  649. }
  650. public static void export(HttpServletResponse response, String fileName, String sheetName,
  651. List<List<Object>> sheetDataList) {
  652. export(response, fileName, sheetName, sheetDataList, null);
  653. }
  654. public static void export(HttpServletResponse response, String fileName, String sheetName,
  655. List<List<Object>> sheetDataList, Map<Integer, List<String>> selectMap) {
  656. Map<String, List<List<Object>>> map = new HashMap<>();
  657. map.put(sheetName, sheetDataList);
  658. export(response, null, fileName, map, selectMap);
  659. }
  660. public static <T, K> void export(HttpServletResponse response, String fileName, List<T> list, Class<K> template) {
  661. // list 是否为空
  662. boolean lisIsEmpty = list == null || list.isEmpty();
  663. // 如果模板数据为空,且导入的数据为空,则导出空文件
  664. if (template == null && lisIsEmpty) {
  665. exportEmpty(response, fileName);
  666. return;
  667. }
  668. // 如果 list 数据,则导出模板数据
  669. if (lisIsEmpty) {
  670. exportTemplate(response, fileName, template);
  671. return;
  672. }
  673. // 导出数据
  674. List<List<Object>> sheetDataList = getSheetData(list);
  675. export(response, fileName, sheetDataList);
  676. }
  677. public static void export(HttpServletResponse response, String fileName, List<List<Object>> sheetDataList, Map<Integer, List<String>> selectMap) {
  678. export(response, fileName, fileName, sheetDataList, selectMap);
  679. }
  680. private static void export(HttpServletResponse response, File file, String fileName,
  681. Map<String, List<List<Object>>> sheetMap, Map<Integer, List<String>> selectMap) {
  682. // 整个 Excel 表格 book 对象
  683. SXSSFWorkbook book = new SXSSFWorkbook();
  684. // 每个 Sheet 页
  685. Set<Entry<String, List<List<Object>>>> entries = sheetMap.entrySet();
  686. for (Entry<String, List<List<Object>>> entry : entries) {
  687. List<List<Object>> sheetDataList = entry.getValue();
  688. Sheet sheet = book.createSheet(entry.getKey());
  689. Drawing<?> patriarch = sheet.createDrawingPatriarch();
  690. // 设置表头背景色(灰色)
  691. CellStyle headStyle = book.createCellStyle();
  692. headStyle.setFillForegroundColor(IndexedColors.GREY_80_PERCENT.index);
  693. headStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
  694. headStyle.setAlignment(HorizontalAlignment.CENTER);
  695. headStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.index);
  696. // 设置表身背景色(默认色)
  697. CellStyle rowStyle = book.createCellStyle();
  698. rowStyle.setAlignment(HorizontalAlignment.CENTER);
  699. rowStyle.setVerticalAlignment(VerticalAlignment.CENTER);
  700. // 设置表格列宽度(默认为15个字节)
  701. sheet.setDefaultColumnWidth(15);
  702. // 创建合并算法数组
  703. int rowLength = sheetDataList.size();
  704. int columnLength = sheetDataList.get(0).size();
  705. int[][] mergeArray = new int[rowLength][columnLength];
  706. for (int i = 0; i < sheetDataList.size(); i++) {
  707. // 每个 Sheet 页中的行数据
  708. Row row = sheet.createRow(i);
  709. List<Object> rowList = sheetDataList.get(i);
  710. for (int j = 0; j < rowList.size(); j++) {
  711. // 每个行数据中的单元格数据
  712. Object o = rowList.get(j);
  713. int v = 0;
  714. if (o instanceof URL) {
  715. // 如果要导出图片的话, 链接需要传递 URL 对象
  716. setCellPicture(book, row, patriarch, i, j, (URL) o);
  717. } else {
  718. Cell cell = row.createCell(j);
  719. if (i == 0) {
  720. // 第一行为表头行,采用灰色底背景
  721. v = setCellValue(cell, o, headStyle);
  722. } else {
  723. // 其他行为数据行,默认白底色
  724. v = setCellValue(cell, o, rowStyle);
  725. }
  726. }
  727. mergeArray[i][j] = v;
  728. }
  729. }
  730. // 合并单元格
  731. mergeCells(sheet, mergeArray);
  732. // 设置下拉列表
  733. setSelect(sheet, selectMap);
  734. }
  735. // 写数据
  736. if (response != null) {
  737. // 前端导出
  738. try {
  739. write(response, book, fileName);
  740. } catch (IOException e) {
  741. e.printStackTrace();
  742. }
  743. } else {
  744. // 本地导出
  745. FileOutputStream fos;
  746. try {
  747. fos = new FileOutputStream(file);
  748. ByteArrayOutputStream ops = new ByteArrayOutputStream();
  749. book.write(ops);
  750. fos.write(ops.toByteArray());
  751. fos.close();
  752. } catch (Exception e) {
  753. e.printStackTrace();
  754. }
  755. }
  756. }
  757. /**
  758. * 合并当前Sheet页的单元格
  759. *
  760. * @param sheet 当前 sheet 页
  761. * @param mergeArray 合并单元格算法
  762. */
  763. private static void mergeCells(Sheet sheet, int[][] mergeArray) {
  764. // 横向合并
  765. for (int x = 0; x < mergeArray.length; x++) {
  766. int[] arr = mergeArray[x];
  767. boolean merge = false;
  768. int y1 = 0;
  769. int y2 = 0;
  770. for (int y = 0; y < arr.length; y++) {
  771. int value = arr[y];
  772. if (value == CELL_COLUMN_MERGE) {
  773. if (!merge) {
  774. y1 = y;
  775. }
  776. y2 = y;
  777. merge = true;
  778. } else {
  779. merge = false;
  780. if (y1 > 0) {
  781. sheet.addMergedRegion(new CellRangeAddress(x, x, (y1 - 1), y2));
  782. }
  783. y1 = 0;
  784. y2 = 0;
  785. }
  786. }
  787. if (y1 > 0) {
  788. sheet.addMergedRegion(new CellRangeAddress(x, x, (y1 - 1), y2));
  789. }
  790. }
  791. // 纵向合并
  792. int xLen = mergeArray.length;
  793. int yLen = mergeArray[0].length;
  794. for (int y = 0; y < yLen; y++) {
  795. boolean merge = false;
  796. int x1 = 0;
  797. int x2 = 0;
  798. for (int x = 0; x < xLen; x++) {
  799. int value = mergeArray[x][y];
  800. if (value == CELL_ROW_MERGE) {
  801. if (!merge) {
  802. x1 = x;
  803. }
  804. x2 = x;
  805. merge = true;
  806. } else {
  807. merge = false;
  808. if (x1 > 0) {
  809. sheet.addMergedRegion(new CellRangeAddress((x1 - 1), x2, y, y));
  810. }
  811. x1 = 0;
  812. x2 = 0;
  813. }
  814. }
  815. if (x1 > 0) {
  816. sheet.addMergedRegion(new CellRangeAddress((x1 - 1), x2, y, y));
  817. }
  818. }
  819. }
  820. private static void write(HttpServletResponse response, SXSSFWorkbook book, String fileName) throws IOException {
  821. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  822. response.setCharacterEncoding("utf-8");
  823. String name = new String(fileName.getBytes("GBK"), "ISO8859_1") + XLSX;
  824. response.addHeader("Content-Disposition", "attachment;filename=" + name);
  825. ServletOutputStream out = response.getOutputStream();
  826. book.write(out);
  827. out.flush();
  828. out.close();
  829. }
  830. private static int setCellValue(Cell cell, Object o, CellStyle style) {
  831. // 设置样式
  832. cell.setCellStyle(style);
  833. // 数据为空时
  834. if (o == null) {
  835. cell.setCellType(CellType.STRING);
  836. cell.setCellValue("");
  837. return CELL_OTHER;
  838. }
  839. // 是否为字符串
  840. if (o instanceof String) {
  841. String s = o.toString();
  842. // 当数字类型长度超过8位时,改为字符串类型显示(Excel数字超过一定长度会显示为科学计数法)
  843. if (isNumeric(s) && s.length() < 8) {
  844. cell.setCellType(CellType.NUMERIC);
  845. cell.setCellValue(Double.parseDouble(s));
  846. return CELL_OTHER;
  847. } else {
  848. cell.setCellType(CellType.STRING);
  849. cell.setCellValue(s);
  850. }
  851. if (s.equals(ROW_MERGE)) {
  852. return CELL_ROW_MERGE;
  853. } else if (s.equals(COLUMN_MERGE)) {
  854. return CELL_COLUMN_MERGE;
  855. } else {
  856. return CELL_OTHER;
  857. }
  858. }
  859. // 是否为字符串
  860. if (o instanceof Integer || o instanceof Long || o instanceof Double || o instanceof Float) {
  861. cell.setCellType(CellType.NUMERIC);
  862. cell.setCellValue(Double.parseDouble(o.toString()));
  863. return CELL_OTHER;
  864. }
  865. // 是否为Boolean
  866. if (o instanceof Boolean) {
  867. cell.setCellType(CellType.BOOLEAN);
  868. cell.setCellValue((Boolean) o);
  869. return CELL_OTHER;
  870. }
  871. // 如果是BigDecimal,则默认3位小数
  872. if (o instanceof BigDecimal) {
  873. cell.setCellType(CellType.NUMERIC);
  874. cell.setCellValue(((BigDecimal) o).setScale(3, RoundingMode.HALF_UP).doubleValue());
  875. return CELL_OTHER;
  876. }
  877. // 如果是Date数据,则显示格式化数据
  878. if (o instanceof Date) {
  879. cell.setCellType(CellType.STRING);
  880. cell.setCellValue(formatDate((Date) o));
  881. return CELL_OTHER;
  882. }
  883. // 如果是其他,则默认字符串类型
  884. cell.setCellType(CellType.STRING);
  885. cell.setCellValue(o.toString());
  886. return CELL_OTHER;
  887. }
  888. private static void setCellPicture(SXSSFWorkbook wb, Row sr, Drawing<?> patriarch, int x, int y, URL url) {
  889. // 设置图片宽高
  890. sr.setHeight((short) (IMG_WIDTH * IMG_HEIGHT));
  891. // (jdk1.7版本try中定义流可自动关闭)
  892. try (InputStream is = url.openStream(); ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
  893. byte[] buff = new byte[BYTES_DEFAULT_LENGTH];
  894. int rc;
  895. while ((rc = is.read(buff, 0, BYTES_DEFAULT_LENGTH)) > 0) {
  896. outputStream.write(buff, 0, rc);
  897. }
  898. // 设置图片位置
  899. XSSFClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, y, x, y + 1, x + 1);
  900. // 设置这个,图片会自动填满单元格的长宽
  901. anchor.setAnchorType(AnchorType.MOVE_AND_RESIZE);
  902. patriarch.createPicture(anchor, wb.addPicture(outputStream.toByteArray(), HSSFWorkbook.PICTURE_TYPE_JPEG));
  903. } catch (Exception e) {
  904. e.printStackTrace();
  905. }
  906. }
  907. private static String formatDate(Date date) {
  908. if (date == null) {
  909. return "";
  910. }
  911. SimpleDateFormat format = new SimpleDateFormat(DATE_FORMAT);
  912. return format.format(date);
  913. }
  914. private static void setSelect(Sheet sheet, Map<Integer, List<String>> selectMap) {
  915. if (selectMap == null || selectMap.isEmpty()) {
  916. return;
  917. }
  918. Set<Entry<Integer, List<String>>> entrySet = selectMap.entrySet();
  919. for (Entry<Integer, List<String>> entry : entrySet) {
  920. int y = entry.getKey();
  921. List<String> list = entry.getValue();
  922. if (list == null || list.isEmpty()) {
  923. continue;
  924. }
  925. String[] arr = new String[list.size()];
  926. for (int i = 0; i < list.size(); i++) {
  927. arr[i] = list.get(i);
  928. }
  929. DataValidationHelper helper = sheet.getDataValidationHelper();
  930. CellRangeAddressList addressList = new CellRangeAddressList(1, 65000, y, y);
  931. DataValidationConstraint dvc = helper.createExplicitListConstraint(arr);
  932. DataValidation dv = helper.createValidation(dvc, addressList);
  933. if (dv instanceof HSSFDataValidation) {
  934. dv.setSuppressDropDownArrow(false);
  935. } else {
  936. dv.setSuppressDropDownArrow(true);
  937. dv.setShowErrorBox(true);
  938. }
  939. sheet.addValidationData(dv);
  940. }
  941. }
  942. private static boolean isNumeric(String str) {
  943. if (Objects.nonNull(str) && "0.0".equals(str)) {
  944. return true;
  945. }
  946. for (int i = str.length(); --i >= 0; ) {
  947. if (!Character.isDigit(str.charAt(i))) {
  948. return false;
  949. }
  950. }
  951. return true;
  952. }
  953. private static String getString(String s) {
  954. if (s == null) {
  955. return "";
  956. }
  957. if (s.isEmpty()) {
  958. return s;
  959. }
  960. return s.trim();
  961. }
  962. }