当前位置:   article > 正文

Excel工具类_@excelimport

@excelimport

目录

1导入导出

2测试

2.1导入测试

2.1.1JSON导入

2.1.2对象导入

 2.2导出测试

2.2.1导出模版

2.2.2导出用户表

3依赖

4工具包


​​​​​​​

1导入导出

UserImport

  1. package com.excel.entity;
  2. import com.excel.utils.ExcelImport;
  3. import lombok.Data;
  4. @Data
  5. public class UserImport {
  6. // 获取此行的错误提示
  7. private String rowTips;
  8. // 获取此时数据第几行
  9. private int rowNum;
  10. // required非空 unique不能重复
  11. @ExcelImport(value = "姓名",required = true,unique = true)
  12. private String name;
  13. @ExcelImport("年龄")
  14. private Integer age;
  15. @ExcelImport(value = "性别",kv="1-男;2-女")
  16. private Integer sex;
  17. @ExcelImport(value = "电话",maxLength = 11)
  18. private String tel;
  19. @ExcelImport("城市")
  20. private String city;
  21. @ExcelImport("头像")
  22. private String avatar;
  23. }

UserExport

  1. package com.excel.entity;
  2. import com.excel.utils.ExcelExport;
  3. import lombok.Data;
  4. @Data
  5. public class UserExport {
  6. // 注释的是导出模版文件
  7. @ExcelExport(value = "姓名",example = "张三")
  8. private String name;
  9. @ExcelExport(value = "年龄",example = "81")
  10. private Integer age;
  11. @ExcelExport(value="城市",example = "苏州")
  12. private String city;
  13. @ExcelExport(value = "头像",example = "http:dwawaa")
  14. private String avatar;
  15. @ExcelExport(value="电话",example = "1111123232")
  16. private String tel;
  17. // 导出用户表
  18. // @ExcelExport(value = "姓名")
  19. // private String name;
  20. //
  21. // @ExcelExport(value = "年龄")
  22. // private Integer age;
  23. //
  24. // @ExcelExport(value="城市")
  25. // private String city;
  26. // @ExcelExport(value = "头像")
  27. // private String avatar;
  28. //
  29. // @ExcelExport(value="电话")
  30. // private String tel;
  31. }

excelController

  1. package com.excel.controller;
  2. import com.alibaba.fastjson.JSONArray;
  3. import com.excel.entity.UserImport;
  4. import com.excel.entity.UserExport;
  5. import com.excel.utils.ExcelUtil;
  6. import org.springframework.web.bind.annotation.*;
  7. import org.springframework.web.multipart.MultipartFile;
  8. import javax.servlet.http.HttpServletResponse;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. @RestController
  12. @RequestMapping("/user")
  13. public class ExcelController {
  14. //解析为JSON格式 导入
  15. @PostMapping("/import1")
  16. public JSONArray importJSON(@RequestPart("file") MultipartFile file) throws Exception {
  17. JSONArray array = ExcelUtil.readMultipartFile(file);
  18. System.out.println(array);
  19. return array;
  20. }
  21. // 解析为对象格式 导入
  22. @PostMapping("/import2")
  23. public void importUser(@RequestPart("file") MultipartFile file) throws Exception {
  24. List<UserImport> userImports = ExcelUtil.readMultipartFile(file, UserImport.class);
  25. for(UserImport u: userImports){
  26. System.out.println(u);
  27. }
  28. }
  29. // 导出模版
  30. @GetMapping("/export1")
  31. public void export1(HttpServletResponse httpServletResponse){
  32. ExcelUtil.exportTemplate(httpServletResponse,"用户表", UserExport.class,true);
  33. }
  34. // 导出
  35. @GetMapping("/export2")
  36. public void export2(HttpServletResponse httpServletResponse){
  37. ArrayList<UserExport> userList = new ArrayList<>();
  38. UserExport user1 = new UserExport();
  39. user1.setName("hh");
  40. user1.setAge(2);
  41. user1.setAvatar("33");
  42. user1.setCity("3");
  43. user1.setTel("34");
  44. userList.add(user1);
  45. ExcelUtil.export(httpServletResponse,"用户表",userList,UserExport.class);
  46. }
  47. }

2测试

2.1导入测试

2.1.1JSON导入

PostMan测试 

2.1.2对象导入

 2.2导出测试

2.2.1导出模版

直接在浏览器上输入即可

2.2.2导出用户表

 

3依赖

  1. <!-- excel-->
  2. <!-- 文件上传 -->
  3. <dependency>
  4. <groupId>org.apache.httpcomponents</groupId>
  5. <artifactId>httpmime</artifactId>
  6. <artifactId>4.5.7</artifactId>
  7. </dependency>
  8. <!-- JSON -->
  9. <dependency>
  10. <groupId>com.alibaba</groupId>
  11. <artifactId>fastjson</artifactId>
  12. <version>1.2.41</version>
  13. </dependency>
  14. <!-- POI -->
  15. <dependency>
  16. <groupId>org.apache.poi</groupId>
  17. <artifactId>poi-ooxml</artifactId>
  18. <version>3.16</version>
  19. </dependency>
  20. <!-- end-->

4工具包

ExcelClassField

  1. package com.excel.utils;
  2. import java.util.LinkedHashMap;
  3. public class ExcelClassField {
  4. /** 字段名称 */
  5. private String fieldName;
  6. /** 表头名称 */
  7. private String name;
  8. /** 映射关系 */
  9. private LinkedHashMap<String, String> kvMap;
  10. /** 示例值 */
  11. private Object example;
  12. /** 排序 */
  13. private int sort;
  14. /** 是否为注解字段:0-否,1-是 */
  15. private int hasAnnotation;
  16. public String getFieldName() {
  17. return fieldName;
  18. }
  19. public void setFieldName(String fieldName) {
  20. this.fieldName = fieldName;
  21. }
  22. public String getName() {
  23. return name;
  24. }
  25. public void setName(String name) {
  26. this.name = name;
  27. }
  28. public LinkedHashMap<String, String> getKvMap() {
  29. return kvMap;
  30. }
  31. public void setKvMap(LinkedHashMap<String, String> kvMap) {
  32. this.kvMap = kvMap;
  33. }
  34. public Object getExample() {
  35. return example;
  36. }
  37. public void setExample(Object example) {
  38. this.example = example;
  39. }
  40. public int getSort() {
  41. return sort;
  42. }
  43. public void setSort(int sort) {
  44. this.sort = sort;
  45. }
  46. public int getHasAnnotation() {
  47. return hasAnnotation;
  48. }
  49. public void setHasAnnotation(int hasAnnotation) {
  50. this.hasAnnotation = hasAnnotation;
  51. }
  52. }

 ExcelExport

  1. package com.excel.utils;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface ExcelExport {
  9. /** 字段名称 */
  10. String value();
  11. /** 导出排序先后: 数字越小越靠前(默认按Java类字段顺序导出) */
  12. int sort() default 0;
  13. /** 导出映射,格式如:0-未知;1-男;2-女 */
  14. String kv() default "";
  15. /** 导出模板示例值(有值的话,直接取该值,不做映射) */
  16. String example() default "";
  17. }

ExcelImport

  1. package com.excel.utils;
  2. import java.lang.annotation.ElementType;
  3. import java.lang.annotation.Retention;
  4. import java.lang.annotation.RetentionPolicy;
  5. import java.lang.annotation.Target;
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface ExcelImport {
  9. /** 字段名称 */
  10. String value();
  11. /** 导出映射,格式如:0-未知;1-男;2-女 */
  12. String kv() default "";
  13. /** 是否为必填字段(默认为非必填) */
  14. boolean required() default false;
  15. /** 最大长度(默认255*/
  16. int maxLength() default 255;
  17. /** 导入唯一性验证(多个字段则取联合验证) */
  18. boolean unique() default false;
  19. }

ExcelUtil

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/321497
推荐阅读
相关标签
  

闽ICP备14008679号