ExcelUtil.java 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  1. package com.railway.common.utils.poi;
  2. import com.railway.common.annotation.Excel;
  3. import com.railway.common.annotation.Excel.ColumnType;
  4. import com.railway.common.annotation.Excel.Type;
  5. import com.railway.common.annotation.Excels;
  6. import com.railway.common.constant.Constants;
  7. import com.railway.common.core.text.Convert;
  8. import com.railway.common.exception.UtilException;
  9. import com.railway.common.utils.DictUtils;
  10. import com.railway.common.utils.LocalDateUtil;
  11. import com.railway.common.utils.StringUtils;
  12. import com.railway.common.utils.file.FileTypeUtils;
  13. import com.railway.common.utils.file.FileUploadUtils;
  14. import com.railway.common.utils.file.ImageUtils;
  15. import com.railway.common.utils.reflect.ReflectUtils;
  16. import java.io.IOException;
  17. import java.io.InputStream;
  18. import java.io.OutputStream;
  19. import java.lang.reflect.Field;
  20. import java.lang.reflect.Method;
  21. import java.math.BigDecimal;
  22. import java.nio.file.Files;
  23. import java.nio.file.Paths;
  24. import java.text.DecimalFormat;
  25. import java.util.ArrayList;
  26. import java.util.Arrays;
  27. import java.util.Comparator;
  28. import java.util.Date;
  29. import java.util.HashMap;
  30. import java.util.List;
  31. import java.util.Map;
  32. import java.util.Set;
  33. import java.util.UUID;
  34. import java.util.stream.Collectors;
  35. import javax.servlet.http.HttpServletResponse;
  36. import lombok.extern.slf4j.Slf4j;
  37. import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
  38. import org.apache.poi.hssf.usermodel.HSSFPicture;
  39. import org.apache.poi.hssf.usermodel.HSSFPictureData;
  40. import org.apache.poi.hssf.usermodel.HSSFShape;
  41. import org.apache.poi.hssf.usermodel.HSSFSheet;
  42. import org.apache.poi.hssf.usermodel.HSSFWorkbook;
  43. import org.apache.poi.ooxml.POIXMLDocumentPart;
  44. import org.apache.poi.ss.usermodel.BorderStyle;
  45. import org.apache.poi.ss.usermodel.Cell;
  46. import org.apache.poi.ss.usermodel.CellStyle;
  47. import org.apache.poi.ss.usermodel.CellType;
  48. import org.apache.poi.ss.usermodel.ClientAnchor;
  49. import org.apache.poi.ss.usermodel.DataValidation;
  50. import org.apache.poi.ss.usermodel.DataValidationConstraint;
  51. import org.apache.poi.ss.usermodel.DataValidationHelper;
  52. import org.apache.poi.ss.usermodel.DateUtil;
  53. import org.apache.poi.ss.usermodel.Drawing;
  54. import org.apache.poi.ss.usermodel.FillPatternType;
  55. import org.apache.poi.ss.usermodel.Font;
  56. import org.apache.poi.ss.usermodel.HorizontalAlignment;
  57. import org.apache.poi.ss.usermodel.IndexedColors;
  58. import org.apache.poi.ss.usermodel.PictureData;
  59. import org.apache.poi.ss.usermodel.Row;
  60. import org.apache.poi.ss.usermodel.Sheet;
  61. import org.apache.poi.ss.usermodel.VerticalAlignment;
  62. import org.apache.poi.ss.usermodel.Workbook;
  63. import org.apache.poi.ss.usermodel.WorkbookFactory;
  64. import org.apache.poi.ss.util.CellRangeAddress;
  65. import org.apache.poi.ss.util.CellRangeAddressList;
  66. import org.apache.poi.util.IOUtils;
  67. import org.apache.poi.xssf.streaming.SXSSFWorkbook;
  68. import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
  69. import org.apache.poi.xssf.usermodel.XSSFDataValidation;
  70. import org.apache.poi.xssf.usermodel.XSSFDrawing;
  71. import org.apache.poi.xssf.usermodel.XSSFPicture;
  72. import org.apache.poi.xssf.usermodel.XSSFShape;
  73. import org.apache.poi.xssf.usermodel.XSSFSheet;
  74. import org.openxmlformats.schemas.drawingml.x2006.spreadsheetDrawing.CTMarker;
  75. /**
  76. * Excel相关处理
  77. *
  78. * @author railway
  79. */
  80. @Slf4j
  81. public class ExcelUtil<T> {
  82. /**
  83. * Excel sheet最大行数,默认65536
  84. */
  85. public static final int sheetSize = 65536;
  86. /**
  87. * 工作表名称
  88. */
  89. private String sheetName;
  90. /**
  91. * 导出类型(EXPORT:导出数据;IMPORT:导入模板)
  92. */
  93. private Type type;
  94. /**
  95. * 工作薄对象
  96. */
  97. private Workbook wb;
  98. /**
  99. * 工作表对象
  100. */
  101. private Sheet sheet;
  102. /**
  103. * 样式列表
  104. */
  105. private Map<String, CellStyle> styles;
  106. /**
  107. * 导入导出数据列表
  108. */
  109. private List<T> list;
  110. /**
  111. * 注解列表
  112. */
  113. private List<Object[]> fields;
  114. /**
  115. * 当前行号
  116. */
  117. private int rownum;
  118. /**
  119. * 标题
  120. */
  121. private String title;
  122. /**
  123. * 最大高度
  124. */
  125. private short maxHeight;
  126. /**
  127. * 统计列表
  128. */
  129. private final Map<Integer, Double> statistics = new HashMap<>();
  130. /**
  131. * 数字格式
  132. */
  133. private static final DecimalFormat DOUBLE_FORMAT = new DecimalFormat("######0.00");
  134. /**
  135. * 实体对象
  136. */
  137. public Class<T> clazz;
  138. public ExcelUtil(Class<T> clazz) {
  139. this.clazz = clazz;
  140. }
  141. public void init(List<T> list, String sheetName, String title, Type type) {
  142. if (list == null) {
  143. list = new ArrayList<>();
  144. }
  145. this.list = list;
  146. this.sheetName = sheetName;
  147. this.type = type;
  148. this.title = title;
  149. createExcelField();
  150. createWorkbook();
  151. createTitle();
  152. }
  153. /**
  154. * 创建excel第一行标题
  155. */
  156. public void createTitle() {
  157. if (StringUtils.isNotEmpty(title)) {
  158. Row titleRow = sheet.createRow(rownum == 0 ? rownum++ : 0);
  159. titleRow.setHeightInPoints(30);
  160. Cell titleCell = titleRow.createCell(0);
  161. titleCell.setCellStyle(styles.get("title"));
  162. titleCell.setCellValue(title);
  163. sheet.addMergedRegion(
  164. new CellRangeAddress(titleRow.getRowNum(), titleRow.getRowNum(), titleRow.getRowNum(),
  165. this.fields.size() - 1));
  166. }
  167. }
  168. /**
  169. * 对excel表单默认第一个索引名转换成list
  170. *
  171. * @param is 输入流
  172. * @return 转换后集合
  173. */
  174. public List<T> importExcel(InputStream is) throws Exception {
  175. return importExcel(is, 0);
  176. }
  177. /**
  178. * 对excel表单默认第一个索引名转换成list
  179. *
  180. * @param is 输入流
  181. * @param titleNum 标题占用行数
  182. * @return 转换后集合
  183. */
  184. public List<T> importExcel(InputStream is, int titleNum) throws Exception {
  185. return importExcel(StringUtils.EMPTY, is, titleNum);
  186. }
  187. /**
  188. * 对excel表单指定表格索引名转换成list
  189. *
  190. * @param sheetName 表格索引名
  191. * @param titleNum 标题占用行数
  192. * @param is 输入流
  193. * @return 转换后集合
  194. */
  195. public List<T> importExcel(String sheetName, InputStream is, int titleNum) throws Exception {
  196. this.type = Type.IMPORT;
  197. this.wb = WorkbookFactory.create(is);
  198. List<T> list = new ArrayList<>();
  199. // 如果指定sheet名,则取指定sheet中的内容 否则默认指向第1个sheet
  200. Sheet sheet = StringUtils.isNotEmpty(sheetName) ? wb.getSheet(sheetName) : wb.getSheetAt(0);
  201. if (sheet == null) {
  202. throw new IOException("文件sheet不存在");
  203. }
  204. // 获取最后一个非空行的行下标,比如总行数为n,则返回的为n-1
  205. int rows = sheet.getLastRowNum();
  206. if (rows > 0) {
  207. // 定义一个map用于存放excel列的序号和field.
  208. Map<String, Integer> cellMap = new HashMap<>();
  209. // 获取表头
  210. Row heard = sheet.getRow(titleNum);
  211. for (int i = 0; i < heard.getPhysicalNumberOfCells(); i++) {
  212. Cell cell = heard.getCell(i);
  213. if (StringUtils.isNotNull(cell)) {
  214. String value = this.getCellValue(heard, i).toString();
  215. cellMap.put(value, i);
  216. } else {
  217. cellMap.put(null, i);
  218. }
  219. }
  220. // 有数据时才处理 得到类的所有field.
  221. Field[] allFields = clazz.getDeclaredFields();
  222. // 定义一个map用于存放列的序号和field.
  223. Map<Integer, Field> fieldsMap = new HashMap<>();
  224. for (Field field : allFields) {
  225. Excel attr = field.getAnnotation(Excel.class);
  226. if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
  227. // 设置类的私有字段属性可访问.
  228. field.setAccessible(true);
  229. Integer column = cellMap.get(attr.name());
  230. if (column != null) {
  231. fieldsMap.put(column, field);
  232. }
  233. }
  234. }
  235. for (int i = titleNum + 1; i <= rows; i++) {
  236. // 从第2行开始取数据,默认第一行是表头.
  237. Row row = sheet.getRow(i);
  238. // 判断当前行是否是空行
  239. if (isRowEmpty(row)) {
  240. continue;
  241. }
  242. T entity = null;
  243. for (Map.Entry<Integer, Field> entry : fieldsMap.entrySet()) {
  244. Object val = this.getCellValue(row, entry.getKey());
  245. // 如果不存在实例则新建.
  246. entity = (entity == null ? clazz.newInstance() : entity);
  247. // 从map中得到对应列的field.
  248. Field field = fieldsMap.get(entry.getKey());
  249. // 取得类型,并根据对象类型设置值.
  250. Class<?> fieldType = field.getType();
  251. if (String.class == fieldType) {
  252. String s = Convert.toStr(val);
  253. if (StringUtils.endsWith(s, ".0")) {
  254. val = StringUtils.substringBefore(s, ".0");
  255. } else {
  256. String dateFormat = field.getAnnotation(Excel.class).dateFormat();
  257. if (StringUtils.isNotEmpty(dateFormat)) {
  258. val = LocalDateUtil.convertLocalDateTimeToString(String.valueOf(val), dateFormat);
  259. } else {
  260. val = Convert.toStr(val);
  261. }
  262. }
  263. } else if ((Integer.TYPE == fieldType || Integer.class == fieldType)
  264. && StringUtils.isNumeric(Convert.toStr(val))) {
  265. val = Convert.toInt(val);
  266. } else if (Long.TYPE == fieldType || Long.class == fieldType) {
  267. val = Convert.toLong(val);
  268. } else if (Double.TYPE == fieldType || Double.class == fieldType) {
  269. val = Convert.toDouble(val);
  270. } else if (Float.TYPE == fieldType || Float.class == fieldType) {
  271. val = Convert.toFloat(val);
  272. } else if (BigDecimal.class == fieldType) {
  273. val = Convert.toBigDecimal(val);
  274. } else if (Date.class == fieldType) {
  275. if (val instanceof String) {
  276. val = LocalDateUtil.parseDate(val);
  277. } else if (val instanceof Double) {
  278. val = DateUtil.getJavaDate((Double) val);
  279. }
  280. } else if (Boolean.TYPE == fieldType || Boolean.class == fieldType) {
  281. val = Convert.toBool(val, false);
  282. }
  283. Excel attr = field.getAnnotation(Excel.class);
  284. String propertyName = field.getName();
  285. if (StringUtils.isNotEmpty(attr.targetAttr())) {
  286. propertyName = field.getName() + "." + attr.targetAttr();
  287. } else if (StringUtils.isNotEmpty(attr.readConverterExp())) {
  288. val = reverseByExp(Convert.toStr(val), attr.readConverterExp(), attr.separator());
  289. } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
  290. val = dataFormatHandlerAdapter(val, attr);
  291. }
  292. ReflectUtils.invokeSetter(entity, propertyName, val);
  293. }
  294. list.add(entity);
  295. }
  296. }
  297. return list;
  298. }
  299. /**
  300. * 对list数据源将其里面的数据导入到excel表单
  301. *
  302. * @param list 导出数据集合
  303. * @param sheetName 工作表的名称
  304. * @return 结果
  305. */
  306. public String exportExcel(List<T> list, String sheetName) {
  307. return exportExcel(list, sheetName, StringUtils.EMPTY);
  308. }
  309. /**
  310. * 对list数据源将其里面的数据导入到excel表单
  311. *
  312. * @param list 导出数据集合
  313. * @param sheetName 工作表的名称
  314. * @param title 标题
  315. * @return 结果
  316. */
  317. public String exportExcel(List<T> list, String sheetName, String title) {
  318. this.init(list, sheetName, title, Type.EXPORT);
  319. return exportExcel();
  320. }
  321. /**
  322. * 对list数据源将其里面的数据导入到excel表单
  323. *
  324. * @param response 返回数据
  325. * @param list 导出数据集合
  326. * @param sheetName 工作表的名称
  327. */
  328. public void exportExcel(HttpServletResponse response, List<T> list, String sheetName)
  329. throws IOException {
  330. exportExcel(response, list, sheetName, StringUtils.EMPTY);
  331. }
  332. /**
  333. * 对list数据源将其里面的数据导入到excel表单
  334. *
  335. * @param response 返回数据
  336. * @param list 导出数据集合
  337. * @param sheetName 工作表的名称
  338. * @param title 标题
  339. */
  340. public void exportExcel(HttpServletResponse response, List<T> list, String sheetName,
  341. String title) throws IOException {
  342. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  343. response.setCharacterEncoding("utf-8");
  344. this.init(list, sheetName, title, Type.EXPORT);
  345. exportExcel(response.getOutputStream());
  346. }
  347. /**
  348. * 对list数据源将其里面的数据导入到excel表单
  349. *
  350. * @param sheetName 工作表的名称
  351. * @return 结果
  352. */
  353. public String importTemplateExcel(String sheetName) {
  354. return importTemplateExcel(sheetName, StringUtils.EMPTY);
  355. }
  356. /**
  357. * 对list数据源将其里面的数据导入到excel表单
  358. *
  359. * @param sheetName 工作表的名称
  360. * @param title 标题
  361. * @return 结果
  362. */
  363. public String importTemplateExcel(String sheetName, String title) {
  364. this.init(null, sheetName, title, Type.IMPORT);
  365. return exportExcel();
  366. }
  367. /**
  368. * 对list数据源将其里面的数据导入到excel表单
  369. *
  370. * @param sheetName 工作表的名称
  371. * @param title 标题
  372. */
  373. public void importTemplateExcel(HttpServletResponse response, String sheetName, String title)
  374. throws IOException {
  375. response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
  376. response.setCharacterEncoding("utf-8");
  377. this.init(null, sheetName, title, Type.IMPORT);
  378. exportExcel(response.getOutputStream());
  379. }
  380. /**
  381. * 对list数据源将其里面的数据导入到excel表单
  382. */
  383. public void exportExcel(OutputStream out) {
  384. try {
  385. writeSheet();
  386. wb.write(out);
  387. } catch (Exception e) {
  388. log.error("导出Excel异常{}", e.getMessage());
  389. } finally {
  390. IOUtils.closeQuietly(wb);
  391. IOUtils.closeQuietly(out);
  392. }
  393. }
  394. /**
  395. * 对list数据源将其里面的数据导入到excel表单
  396. *
  397. * @return 结果
  398. */
  399. public String exportExcel() {
  400. OutputStream out = null;
  401. try {
  402. writeSheet();
  403. String filename = encodingFilename(sheetName);
  404. String filePath = FileUploadUtils.getAbsoluteFile(filename);
  405. out = Files.newOutputStream(Paths.get(filePath));
  406. wb.write(out);
  407. return filePath;
  408. } catch (Exception e) {
  409. log.error("导出Excel异常{}", e.getMessage());
  410. throw new UtilException("导出Excel失败,请联系网站管理员!");
  411. } finally {
  412. IOUtils.closeQuietly(wb);
  413. IOUtils.closeQuietly(out);
  414. }
  415. }
  416. /**
  417. * 创建写入数据到Sheet
  418. */
  419. public void writeSheet() {
  420. // 取出一共有多少个sheet.
  421. int sheetNo = Math.max(1, (int) Math.ceil(list.size() * 1.0 / sheetSize));
  422. for (int index = 0; index < sheetNo; index++) {
  423. createSheet(sheetNo, index);
  424. // 产生一行
  425. Row row = sheet.createRow(rownum);
  426. int column = 0;
  427. // 写入各个字段的列头名称
  428. for (Object[] os : fields) {
  429. Excel excel = (Excel) os[1];
  430. this.createCell(excel, row, column++);
  431. }
  432. if (Type.EXPORT.equals(type)) {
  433. fillExcelData(index);
  434. addStatisticsRow();
  435. }
  436. }
  437. }
  438. /**
  439. * 填充excel数据
  440. * @param index 序号
  441. *
  442. */
  443. public void fillExcelData(int index) {
  444. int startNo = index * sheetSize;
  445. int endNo = Math.min(startNo + sheetSize, list.size());
  446. for (int i = startNo; i < endNo; i++) {
  447. Row row = sheet.createRow(i + 1 + rownum - startNo);
  448. // 得到导出对象.
  449. T vo = list.get(i);
  450. int column = 0;
  451. for (Object[] os : fields) {
  452. Field field = (Field) os[0];
  453. Excel excel = (Excel) os[1];
  454. // 设置实体类私有属性可访问
  455. field.setAccessible(true);
  456. this.addCell(excel, row, vo, field, column++);
  457. }
  458. }
  459. }
  460. /**
  461. * 创建表格样式
  462. *
  463. * @param wb 工作薄对象
  464. * @return 样式列表
  465. */
  466. private Map<String, CellStyle> createStyles(Workbook wb) {
  467. // 写入各条记录,每条记录对应excel表中的一行
  468. Map<String, CellStyle> styles = new HashMap<>();
  469. CellStyle style = wb.createCellStyle();
  470. style.setAlignment(HorizontalAlignment.CENTER);
  471. style.setVerticalAlignment(VerticalAlignment.CENTER);
  472. Font titleFont = wb.createFont();
  473. titleFont.setFontName("Arial");
  474. titleFont.setFontHeightInPoints((short) 16);
  475. titleFont.setBold(true);
  476. style.setFont(titleFont);
  477. styles.put("title", style);
  478. style = wb.createCellStyle();
  479. style.setAlignment(HorizontalAlignment.CENTER);
  480. style.setVerticalAlignment(VerticalAlignment.CENTER);
  481. style.setBorderRight(BorderStyle.THIN);
  482. style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
  483. style.setBorderLeft(BorderStyle.THIN);
  484. style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
  485. style.setBorderTop(BorderStyle.THIN);
  486. style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
  487. style.setBorderBottom(BorderStyle.THIN);
  488. style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
  489. Font dataFont = wb.createFont();
  490. dataFont.setFontName("Arial");
  491. dataFont.setFontHeightInPoints((short) 10);
  492. style.setFont(dataFont);
  493. styles.put("data", style);
  494. style = wb.createCellStyle();
  495. style.cloneStyleFrom(styles.get("data"));
  496. style.setAlignment(HorizontalAlignment.CENTER);
  497. style.setVerticalAlignment(VerticalAlignment.CENTER);
  498. style.setFillForegroundColor(IndexedColors.GREY_50_PERCENT.getIndex());
  499. style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
  500. Font headerFont = wb.createFont();
  501. headerFont.setFontName("Arial");
  502. headerFont.setFontHeightInPoints((short) 10);
  503. headerFont.setBold(true);
  504. headerFont.setColor(IndexedColors.WHITE.getIndex());
  505. style.setFont(headerFont);
  506. styles.put("header", style);
  507. style = wb.createCellStyle();
  508. style.setAlignment(HorizontalAlignment.CENTER);
  509. style.setVerticalAlignment(VerticalAlignment.CENTER);
  510. Font totalFont = wb.createFont();
  511. totalFont.setFontName("Arial");
  512. totalFont.setFontHeightInPoints((short) 10);
  513. style.setFont(totalFont);
  514. styles.put("total", style);
  515. style = wb.createCellStyle();
  516. style.cloneStyleFrom(styles.get("data"));
  517. style.setAlignment(HorizontalAlignment.LEFT);
  518. styles.put("data1", style);
  519. style = wb.createCellStyle();
  520. style.cloneStyleFrom(styles.get("data"));
  521. style.setAlignment(HorizontalAlignment.CENTER);
  522. styles.put("data2", style);
  523. style = wb.createCellStyle();
  524. style.cloneStyleFrom(styles.get("data"));
  525. style.setAlignment(HorizontalAlignment.RIGHT);
  526. styles.put("data3", style);
  527. return styles;
  528. }
  529. /**
  530. * 创建单元格
  531. */
  532. public Cell createCell(Excel attr, Row row, int column) {
  533. // 创建列
  534. Cell cell = row.createCell(column);
  535. // 写入列信息
  536. cell.setCellValue(attr.name());
  537. setDataValidation(attr, row, column);
  538. cell.setCellStyle(styles.get("header"));
  539. return cell;
  540. }
  541. /**
  542. * 设置单元格信息
  543. *
  544. * @param value 单元格值
  545. * @param attr 注解相关
  546. * @param cell 单元格信息
  547. */
  548. public void setCellVo(Object value, Excel attr, Cell cell) {
  549. if (ColumnType.STRING == attr.cellType()) {
  550. cell.setCellValue(StringUtils.isNull(value) ? attr.defaultValue() : value + attr.suffix());
  551. } else if (ColumnType.NUMERIC == attr.cellType()) {
  552. if (StringUtils.isNotNull(value)) {
  553. cell.setCellValue(StringUtils.contains(Convert.toStr(value), ".") ? Convert.toDouble(value)
  554. : Convert.toInt(value));
  555. }
  556. } else if (ColumnType.IMAGE == attr.cellType()) {
  557. ClientAnchor anchor = new XSSFClientAnchor(0, 0, 0, 0, (short) cell.getColumnIndex(),
  558. cell.getRow().getRowNum(), (short) (cell.getColumnIndex() + 1),
  559. cell.getRow().getRowNum() + 1);
  560. String imagePath = Convert.toStr(value);
  561. if (StringUtils.isNotEmpty(imagePath)) {
  562. byte[] data = ImageUtils.getImage(imagePath);
  563. getDrawingPatriarch(cell.getSheet()).createPicture(anchor,
  564. cell.getSheet().getWorkbook().addPicture(data, getImageType(data)));
  565. }
  566. }
  567. }
  568. /**
  569. * 获取画布
  570. */
  571. public static Drawing<?> getDrawingPatriarch(Sheet sheet) {
  572. if (sheet.getDrawingPatriarch() == null) {
  573. sheet.createDrawingPatriarch();
  574. }
  575. return sheet.getDrawingPatriarch();
  576. }
  577. /**
  578. * 获取图片类型,设置图片插入类型
  579. */
  580. public int getImageType(byte[] value) {
  581. String type = FileTypeUtils.getFileExtendName(value);
  582. if ("JPG".equalsIgnoreCase(type)) {
  583. return Workbook.PICTURE_TYPE_JPEG;
  584. } else if ("PNG".equalsIgnoreCase(type)) {
  585. return Workbook.PICTURE_TYPE_PNG;
  586. }
  587. return Workbook.PICTURE_TYPE_JPEG;
  588. }
  589. /**
  590. * 创建表格样式
  591. */
  592. public void setDataValidation(Excel attr, Row row, int column) {
  593. if (attr.name().contains("注:")) {
  594. sheet.setColumnWidth(column, 6000);
  595. } else {
  596. // 设置列宽
  597. sheet.setColumnWidth(column, (int) ((attr.width() + 0.72) * 256));
  598. }
  599. // 如果设置了提示信息则鼠标放上去提示.
  600. if (StringUtils.isNotEmpty(attr.prompt())) {
  601. // 这里默认设了2-101列提示.
  602. setXSSFPrompt(sheet, "", attr.prompt(), 1, 100, column, column);
  603. }
  604. // 如果设置了combo属性则本列只能选择不能输入
  605. if (attr.combo().length > 0) {
  606. // 这里默认设了2-101列只能选择不能输入.
  607. setXSSFValidation(sheet, attr.combo(), 1, 100, column, column);
  608. }
  609. }
  610. /**
  611. * 添加单元格
  612. */
  613. public Cell addCell(Excel attr, Row row, T vo, Field field, int column) {
  614. Cell cell = null;
  615. try {
  616. // 设置行高
  617. row.setHeight(maxHeight);
  618. // 根据Excel中设置情况决定是否导出,有些情况需要保持为空,希望用户填写这一列.
  619. if (attr.isExport()) {
  620. // 创建cell
  621. cell = row.createCell(column);
  622. int align = attr.align().value();
  623. cell.setCellStyle(styles.get("data" + (align >= 1 && align <= 3 ? align : "")));
  624. // 用于读取对象中的属性
  625. Object value = getTargetValue(vo, field, attr);
  626. String dateFormat = attr.dateFormat();
  627. String readConverterExp = attr.readConverterExp();
  628. String separator = attr.separator();
  629. String dictType = attr.dictType();
  630. if (StringUtils.isNotEmpty(dateFormat) && StringUtils.isNotNull(value)) {
  631. cell.setCellValue(
  632. LocalDateUtil.convertLocalDateTimeToString(String.valueOf(value), dateFormat));
  633. } else if (StringUtils.isNotEmpty(readConverterExp) && StringUtils.isNotNull(value)) {
  634. cell.setCellValue(convertByExp(Convert.toStr(value), readConverterExp, separator));
  635. } else if (StringUtils.isNotEmpty(dictType) && StringUtils.isNotNull(value)) {
  636. cell.setCellValue(convertDictByExp(Convert.toStr(value), dictType, separator));
  637. } else if (value instanceof BigDecimal && -1 != attr.scale()) {
  638. cell.setCellValue(
  639. (((BigDecimal) value).setScale(attr.scale(), attr.roundingMode())).toString());
  640. } else if (!attr.handler().equals(ExcelHandlerAdapter.class)) {
  641. cell.setCellValue(dataFormatHandlerAdapter(value, attr));
  642. } else {
  643. // 设置列类型
  644. setCellVo(value, attr, cell);
  645. }
  646. addStatisticsData(column, Convert.toStr(value), attr);
  647. }
  648. } catch (Exception e) {
  649. log.error("导出Excel失败{}", e.getMessage());
  650. }
  651. return cell;
  652. }
  653. /**
  654. * 设置 POI XSSFSheet 单元格提示
  655. *
  656. * @param sheet 表单
  657. * @param promptTitle 提示标题
  658. * @param promptContent 提示内容
  659. * @param firstRow 开始行
  660. * @param endRow 结束行
  661. * @param firstCol 开始列
  662. * @param endCol 结束列
  663. */
  664. public void setXSSFPrompt(Sheet sheet, String promptTitle, String promptContent, int firstRow,
  665. int endRow,
  666. int firstCol, int endCol) {
  667. DataValidationHelper helper = sheet.getDataValidationHelper();
  668. DataValidationConstraint constraint = helper.createCustomConstraint("DD1");
  669. CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
  670. DataValidation dataValidation = helper.createValidation(constraint, regions);
  671. dataValidation.createPromptBox(promptTitle, promptContent);
  672. dataValidation.setShowPromptBox(true);
  673. sheet.addValidationData(dataValidation);
  674. }
  675. /**
  676. * 设置某些列的值只能输入预制的数据,显示下拉框.
  677. *
  678. * @param sheet 要设置的sheet.
  679. * @param textlist 下拉框显示的内容
  680. * @param firstRow 开始行
  681. * @param endRow 结束行
  682. * @param firstCol 开始列
  683. * @param endCol 结束列
  684. */
  685. public void setXSSFValidation(Sheet sheet, String[] textlist, int firstRow, int endRow,
  686. int firstCol, int endCol) {
  687. DataValidationHelper helper = sheet.getDataValidationHelper();
  688. // 加载下拉列表内容
  689. DataValidationConstraint constraint = helper.createExplicitListConstraint(textlist);
  690. // 设置数据有效性加载在哪个单元格上,四个参数分别是:起始行、终止行、起始列、终止列
  691. CellRangeAddressList regions = new CellRangeAddressList(firstRow, endRow, firstCol, endCol);
  692. // 数据有效性对象
  693. DataValidation dataValidation = helper.createValidation(constraint, regions);
  694. // 处理Excel兼容性问题
  695. if (dataValidation instanceof XSSFDataValidation) {
  696. dataValidation.setSuppressDropDownArrow(true);
  697. dataValidation.setShowErrorBox(true);
  698. } else {
  699. dataValidation.setSuppressDropDownArrow(false);
  700. }
  701. sheet.addValidationData(dataValidation);
  702. }
  703. /**
  704. * 解析导出值 0=男,1=女,2=未知
  705. *
  706. * @param propertyValue 参数值
  707. * @param converterExp 翻译注解
  708. * @param separator 分隔符
  709. * @return 解析后值
  710. */
  711. public static String convertByExp(String propertyValue, String converterExp, String separator) {
  712. StringBuilder propertyString = new StringBuilder();
  713. String[] convertSource = converterExp.split(",");
  714. for (String item : convertSource) {
  715. String[] itemArray = item.split("=");
  716. if (StringUtils.containsAny(separator, propertyValue)) {
  717. for (String value : propertyValue.split(separator)) {
  718. if (itemArray[0].equals(value)) {
  719. propertyString.append(itemArray[1]).append(separator);
  720. break;
  721. }
  722. }
  723. } else {
  724. if (itemArray[0].equals(propertyValue)) {
  725. return itemArray[1];
  726. }
  727. }
  728. }
  729. return StringUtils.stripEnd(propertyString.toString(), separator);
  730. }
  731. /**
  732. * 反向解析值 男=0,女=1,未知=2
  733. *
  734. * @param propertyValue 参数值
  735. * @param converterExp 翻译注解
  736. * @param separator 分隔符
  737. * @return 解析后值
  738. */
  739. public static String reverseByExp(String propertyValue, String converterExp, String separator) {
  740. StringBuilder propertyString = new StringBuilder();
  741. String[] convertSource = converterExp.split(",");
  742. for (String item : convertSource) {
  743. String[] itemArray = item.split("=");
  744. if (StringUtils.containsAny(separator, propertyValue)) {
  745. for (String value : propertyValue.split(separator)) {
  746. if (itemArray[1].equals(value)) {
  747. propertyString.append(itemArray[0]).append(separator);
  748. break;
  749. }
  750. }
  751. } else {
  752. if (itemArray[1].equals(propertyValue)) {
  753. return itemArray[0];
  754. }
  755. }
  756. }
  757. return StringUtils.stripEnd(propertyString.toString(), separator);
  758. }
  759. /**
  760. * 解析字典值
  761. *
  762. * @param dictValue 字典值
  763. * @param dictType 字典类型
  764. * @param separator 分隔符
  765. * @return 字典标签
  766. */
  767. public static String convertDictByExp(String dictValue, String dictType, String separator) {
  768. return DictUtils.getDictLabel(dictType, dictValue, separator);
  769. }
  770. /**
  771. * 反向解析值字典值
  772. *
  773. * @param dictLabel 字典标签
  774. * @param dictType 字典类型
  775. * @param separator 分隔符
  776. * @return 字典值
  777. */
  778. public static String reverseDictByExp(String dictLabel, String dictType, String separator) {
  779. return DictUtils.getDictValue(dictType, dictLabel, separator);
  780. }
  781. /**
  782. * 数据处理器
  783. *
  784. * @param value 数据值
  785. * @param excel 数据注解
  786. */
  787. public String dataFormatHandlerAdapter(Object value, Excel excel) {
  788. try {
  789. Object instance = excel.handler().newInstance();
  790. Method formatMethod = excel.handler()
  791. .getMethod("format", Object.class, String[].class);
  792. value = formatMethod.invoke(instance, value, excel.args());
  793. } catch (Exception e) {
  794. log.error("不能格式化数据 {}, Message = {}", excel.handler(), e.getMessage());
  795. }
  796. return Convert.toStr(value);
  797. }
  798. /**
  799. * 合计统计信息
  800. */
  801. private void addStatisticsData(Integer index, String text, Excel entity) {
  802. if (entity != null && entity.isStatistics()) {
  803. Double temp = 0D;
  804. if (!statistics.containsKey(index)) {
  805. statistics.put(index, temp);
  806. }
  807. try {
  808. temp = Double.valueOf(text);
  809. } catch (NumberFormatException ignored) {
  810. }
  811. statistics.put(index, statistics.get(index) + temp);
  812. }
  813. }
  814. /**
  815. * 创建统计行
  816. */
  817. public void addStatisticsRow() {
  818. if (statistics.size() > 0) {
  819. Row row = sheet.createRow(sheet.getLastRowNum() + 1);
  820. Set<Integer> keys = statistics.keySet();
  821. Cell cell = row.createCell(0);
  822. cell.setCellStyle(styles.get("total"));
  823. cell.setCellValue("合计");
  824. for (Integer key : keys) {
  825. cell = row.createCell(key);
  826. cell.setCellStyle(styles.get("total"));
  827. cell.setCellValue(DOUBLE_FORMAT.format(statistics.get(key)));
  828. }
  829. statistics.clear();
  830. }
  831. }
  832. /**
  833. * 编码文件名
  834. */
  835. public String encodingFilename(String filename) {
  836. filename = UUID.randomUUID() + Constants.UNDERLINE + filename + ".xlsx";
  837. return filename;
  838. }
  839. /**
  840. * 获取bean中的属性值
  841. *
  842. * @param vo 实体对象
  843. * @param field 字段
  844. * @param excel 注解
  845. * @return 最终的属性值
  846. */
  847. private Object getTargetValue(T vo, Field field, Excel excel) throws Exception {
  848. Object o = field.get(vo);
  849. if (StringUtils.isNotEmpty(excel.targetAttr())) {
  850. String target = excel.targetAttr();
  851. if (target.contains(".")) {
  852. String[] targets = target.split("[.]");
  853. for (String name : targets) {
  854. o = getValue(o, name);
  855. }
  856. } else {
  857. o = getValue(o, target);
  858. }
  859. }
  860. return o;
  861. }
  862. /**
  863. * 以类的属性的get方法方法形式获取值
  864. *
  865. * @return value
  866. */
  867. private Object getValue(Object o, String name) throws Exception {
  868. if (StringUtils.isNotNull(o) && StringUtils.isNotEmpty(name)) {
  869. Class<?> clazz = o.getClass();
  870. Field field = clazz.getDeclaredField(name);
  871. field.setAccessible(true);
  872. o = field.get(o);
  873. }
  874. return o;
  875. }
  876. /**
  877. * 得到所有定义字段
  878. */
  879. private void createExcelField() {
  880. this.fields = new ArrayList<>();
  881. List<Field> tempFields = new ArrayList<>();
  882. tempFields.addAll(Arrays.asList(clazz.getSuperclass().getDeclaredFields()));
  883. tempFields.addAll(Arrays.asList(clazz.getDeclaredFields()));
  884. for (Field field : tempFields) {
  885. // 单注解
  886. if (field.isAnnotationPresent(Excel.class)) {
  887. putToField(field, field.getAnnotation(Excel.class));
  888. }
  889. // 多注解
  890. if (field.isAnnotationPresent(Excels.class)) {
  891. Excels attrs = field.getAnnotation(Excels.class);
  892. Excel[] excels = attrs.value();
  893. for (Excel excel : excels) {
  894. putToField(field, excel);
  895. }
  896. }
  897. }
  898. this.fields = this.fields.stream()
  899. .sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort()))
  900. .collect(Collectors.toList());
  901. this.maxHeight = getRowHeight();
  902. }
  903. /**
  904. * 根据注解获取最大行高
  905. */
  906. public short getRowHeight() {
  907. double maxHeight = 0;
  908. for (Object[] os : this.fields) {
  909. Excel excel = (Excel) os[1];
  910. maxHeight = Math.max(maxHeight, excel.height());
  911. }
  912. return (short) (maxHeight * 20);
  913. }
  914. /**
  915. * 放到字段集合中
  916. */
  917. private void putToField(Field field, Excel attr) {
  918. if (attr != null && (attr.type() == Type.ALL || attr.type() == type)) {
  919. this.fields.add(new Object[]{field, attr});
  920. }
  921. }
  922. /**
  923. * 创建一个工作簿
  924. */
  925. public void createWorkbook() {
  926. this.wb = new SXSSFWorkbook(500);
  927. this.sheet = wb.createSheet();
  928. wb.setSheetName(0, sheetName);
  929. this.styles = createStyles(wb);
  930. }
  931. /**
  932. * 创建工作表
  933. *
  934. * @param sheetNo sheet数量
  935. * @param index 序号
  936. */
  937. public void createSheet(int sheetNo, int index) {
  938. // 设置工作表的名称.
  939. if (sheetNo > 1 && index > 0) {
  940. this.sheet = wb.createSheet();
  941. this.createTitle();
  942. wb.setSheetName(index, sheetName + index);
  943. }
  944. }
  945. /**
  946. * 获取单元格值
  947. *
  948. * @param row 获取的行
  949. * @param column 获取单元格列号
  950. * @return 单元格值
  951. */
  952. public Object getCellValue(Row row, int column) {
  953. if (row == null) {
  954. return row;
  955. }
  956. Object val = "";
  957. try {
  958. Cell cell = row.getCell(column);
  959. if (StringUtils.isNotNull(cell)) {
  960. if (cell.getCellType() == CellType.NUMERIC || cell.getCellType() == CellType.FORMULA) {
  961. val = cell.getNumericCellValue();
  962. if (DateUtil.isCellDateFormatted(cell)) {
  963. val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
  964. } else {
  965. if ((Double) val % 1 != 0) {
  966. val = new BigDecimal(val.toString());
  967. } else {
  968. val = new DecimalFormat("0").format(val);
  969. }
  970. }
  971. } else if (cell.getCellType() == CellType.STRING) {
  972. val = cell.getStringCellValue();
  973. } else if (cell.getCellType() == CellType.BOOLEAN) {
  974. val = cell.getBooleanCellValue();
  975. } else if (cell.getCellType() == CellType.ERROR) {
  976. val = cell.getErrorCellValue();
  977. }
  978. }
  979. } catch (Exception e) {
  980. return val;
  981. }
  982. return val;
  983. }
  984. /**
  985. * 判断是否是空行
  986. *
  987. * @param row 判断的行
  988. */
  989. private boolean isRowEmpty(Row row) {
  990. if (row == null) {
  991. return true;
  992. }
  993. for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
  994. Cell cell = row.getCell(i);
  995. if (cell != null && cell.getCellType() != CellType.BLANK) {
  996. return false;
  997. }
  998. }
  999. return true;
  1000. }
  1001. /**
  1002. * 获取Excel2003图片
  1003. *
  1004. * @param sheet 当前sheet对象
  1005. * @param workbook 工作簿对象
  1006. * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
  1007. */
  1008. public static Map<String, PictureData> getSheetPictures03(HSSFSheet sheet,
  1009. HSSFWorkbook workbook) {
  1010. Map<String, PictureData> sheetIndexPicMap = new HashMap<>();
  1011. List<HSSFPictureData> pictures = workbook.getAllPictures();
  1012. if (!pictures.isEmpty()) {
  1013. for (HSSFShape shape : sheet.getDrawingPatriarch().getChildren()) {
  1014. HSSFClientAnchor anchor = (HSSFClientAnchor) shape.getAnchor();
  1015. if (shape instanceof HSSFPicture) {
  1016. HSSFPicture pic = (HSSFPicture) shape;
  1017. int pictureIndex = pic.getPictureIndex() - 1;
  1018. HSSFPictureData picData = pictures.get(pictureIndex);
  1019. String picIndex =
  1020. anchor.getRow1() + "_" + String.valueOf(anchor.getCol1());
  1021. sheetIndexPicMap.put(picIndex, picData);
  1022. }
  1023. }
  1024. return sheetIndexPicMap;
  1025. } else {
  1026. return sheetIndexPicMap;
  1027. }
  1028. }
  1029. /**
  1030. * 获取Excel2007图片
  1031. *
  1032. * @param sheet 当前sheet对象
  1033. * @return Map key:图片单元格索引(1_1)String,value:图片流PictureData
  1034. */
  1035. public static Map<String, PictureData> getSheetPictures07(XSSFSheet sheet) {
  1036. Map<String, PictureData> sheetIndexPicMap = new HashMap<>();
  1037. for (POIXMLDocumentPart dr : sheet.getRelations()) {
  1038. if (dr instanceof XSSFDrawing) {
  1039. XSSFDrawing drawing = (XSSFDrawing) dr;
  1040. List<XSSFShape> shapes = drawing.getShapes();
  1041. for (XSSFShape shape : shapes) {
  1042. if (shape instanceof XSSFPicture) {
  1043. XSSFPicture pic = (XSSFPicture) shape;
  1044. XSSFClientAnchor anchor = pic.getPreferredSize();
  1045. CTMarker ctMarker = anchor.getFrom();
  1046. String picIndex = ctMarker.getRow() + "_" + ctMarker.getCol();
  1047. sheetIndexPicMap.put(picIndex, pic.getPictureData());
  1048. }
  1049. }
  1050. }
  1051. }
  1052. return sheetIndexPicMap;
  1053. }
  1054. }