• Java实现Excel导入导出操作详解


    前言

    本次封装是基于 POI 的二次开发,最终使用只需要调用一个工具类中的方法,就能满足业务中绝大部门的导入和导出需求。

    1. 功能测试

    1.1 测试准备

    在做测试前,我们需要將【2. 环境准备】中的四个文件拷贝在工程里(如:我这里均放在了com.zyq.util.excel 包下)。

    1.2 数据导入

    1.2.1 导入解析为JSON

    比如,我们有下面一个表格:

    Controller 代码:

    1. @PostMapping("/import")
    2. public JSONArray importUser(@RequestPart("file")MultipartFile file) throws Exception {
    3. JSONArray array = ExcelUtils.readMultipartFile(file);
    4. System.out.println("导入数据为:" + array);
    5. return array;
    6. }

    测试效果:

    1.2.2 导入解析为对象(基础)

    首先,你需要创建一个与导入表格对应的Java实体对象,并打上对应的Excel解析的导入注解,@ExcelImport注解的value则为表头名称。

    Controller 代码:

    1. @PostMapping("/import")
    2. public void importUser(@RequestPart("file")MultipartFile file) throws Exception {
    3. List users = ExcelUtils.readMultipartFile(file, User.class);
    4. for (User user : users) {
    5. System.out.println(user.toString());
    6. }
    7. }

    测试效果:

    1.2.3 导入解析为对象(字段自动映射)

    对于有的枚举数据,通常我们导入的时候,表格中的数据是值,而在数据保存时,往往用的是键,比如:我们用sex=1可以表示为男,sex=2表示为女,那么我们通过配置也可以达到导入时,数据的自动映射。

    那么,我们只需要将Java实体中的对象sex字段的类型改为对应的数字类型Integer,然后再注解中配置好 kv 属性(属性格式为:键1-值1;键2-值2;键3-值3;.....)

    Cotroller 代码略(和 1.2.2 完全一致)。

    测试效果:可以看到已经自动映射成功了。

    1.2.4 导入解析为对象(获取行号)

    我们在做页面数据导入时,有时候可能需要获取行号,好追踪导入的数据。

    那么,我们只需要在对应的实体中加入一个 int 类型的 rowNum 字段即可。

    Cotroller 代码略(和 1.2.2 完全一致)。

    测试效果:

    1.2.5 导入解析为对象(获取原始数据)

    在做页面数据导入的时候,如果某行存在错误,一般我们会将原始的数据拿出来分析,为什么会造成数据错误。那么,我们在实体类中,增加一个 String 类型的 rowData 字段即可。

    Cotroller 代码略(和 1.2.2 完全一致)。

    测试效果:

    1.2.6 导入解析为对象(获取错误提示)

    当我们在导入数据的时候,如果某行数据存在,字段类型不正确,长度超过最大限制(详见1.2.7),必填字段验证(1.2.8),数据唯一性验证(1.2.9)等一些错误时候,我们可以往对象中添加一个 String 类型的 rowTips 字段,则可以直接拿到对应的错误信息。

    比如,我们将表格中赵子龙的性别改为F(F并不是映射数据),将大乔的性别改为二十八(不能转换为Integer类型数据)。

    Cotroller 代码略(和 1.2.2 完全一致)。

    测试效果:可以看到,我们可以通过 rowTips 直接拿到对应的错误数据提示。

    1.2.7 导入解析为对象(限制字段长度)

    比如,我们手机通常为11为长度,那么不妨限制电话的最大长度位数为11位。

    对应的做法,就是在 @ExcelImport 注解中,设置 maxLength = 11 即可。

    比如,我们将诸葛孔明的电话长度设置为超过11位数的一个字符串。

    Cotroller 代码略(和 1.2.2 完全一致)。

    测试效果:

    1.2.8 导入解析为对象(必填字段验证)

    我们在做数据导入的时候,往往还会有一些必填字段,比如用户的名称,电话。

    那么,我们只需要在 @ExcelImport 注解属性中,加上 required = true 即可。

    我们将诸葛孔明的电话,以及第4行的姓名去掉,进行测试。

    Cotroller 代码略(和 1.2.2 完全一致)。

    测试效果:

    1.2.9 导入解析为对象(数据唯一性验证)

    (1) 单字段唯一性验证

    我们在导入数据的时候,某个字段是具有唯一性的,比如我们这里假设规定姓名不能重复,那么则可以在对应字段的 @ExcelImport 注解上加上 unique = true 属性。

    这里我们构建2条姓名一样的数据进行测试。

    Cotroller 代码略(和 1.2.2 完全一致)。

    测试效果:

    (2)多字段唯一性验证

    如果你导入的数据存在多字段唯一性验证这种情况,只需要将每个对应字段的 @ExcelImport 注解属性中,都加上 required = true 即可。

    比如:我们将姓名和电话两个字段进行联合唯一性验证(即不能存在有名称和电话都一样的数据,单个字段属性重复允许)。

    首先,我们将刚刚(1)的数据进行导入。

    测试效果:可以看到,虽然名称有相同,但电话不相同,所以这里并没有提示唯一性验证错误。

    现在,我们将最后一行的电话也改为和第1行一样的,于是,现在就存在了违背唯一性的两条数据。

    测试效果:可以看到,我们的联合唯一性验证生效了。

    1.3 数据导出

    1.3.1 动态导出(基础)

    这种方式十分灵活,表中的数据,完全自定义设置。

    Controller 代码:

    1. @GetMapping("/export")
    2. public void export(HttpServletResponse response) {
    3. // 表头数据
    4. List<Object> head = Arrays.asList("姓名","年龄","性别","头像");
    5. // 用户1数据
    6. List<Object> user1 = new ArrayList<>();
    7. user1.add("诸葛亮");
    8. user1.add(60);
    9. user1.add("男");
    10. user1.add("https://profile.csdnimg.cn/A/7/3/3_sunnyzyq");
    11. // 用户2数据
    12. List<Object> user2 = new ArrayList<>();
    13. user2.add("大乔");
    14. user2.add(28);
    15. user2.add("女");
    16. user2.add("https://profile.csdnimg.cn/6/1/9/0_m0_48717371");
    17. // 将数据汇总
    18. List<List<Object>> sheetDataList = new ArrayList<>();
    19. sheetDataList.add(head);
    20. sheetDataList.add(user1);
    21. sheetDataList.add(user2);
    22. // 导出数据
    23. ExcelUtils.export(response,"用户表", sheetDataList);
    24. }

    代码截图:

    由于是 get 请求,我们直接在浏览器上输入请求地址即可触发下载。

    打开下载表格,我们可以看到,表中的数据和我们代码组装的顺序一致。

    1.3.2 动态导出(导出图片)

    如果你的导出中,需要将对应图片链接直接显示为图片的话,那么,这里也是可以的,只需要将对应的类型转为 java.net.URL 类型即可(注意:转的时候有异常处理,为了方便演示,我这里直接抛出)

    测试效果:

    1.3.3 动态导出(实现下拉列表)

    我们在做一些数据导出的时候,可能要对某一行的下拉数据进行约束限制。

    比如,当我们下载一个导入模版的时候,我们可以将性别,城市对应的列设置为下拉选择。

    测试效果:

    1.3.4 动态导出(横向合并)

    比如,我们将表头横向合并,只需要将合并的单元格设置为 ExcelUtils.COLUMN_MERGE 即可。

    测试效果:可以看到表头的地址已经被合并了。

    1.3.5 动态导出(纵向合并)

    除了横向合并,我们还可以进行纵向合并,只需要将合并的单元格设置为 ExcelUtils.ROW_MERGE 即可。

    测试效果:

    1.3.6 导出模板(基础)

    我们在做数据导入的时候,往往首先会提供一个模版供其下载,这样用户在导入的时候才知道如何去填写数据。导出模板除了可以用上面的动态导出,这里还提供了一种更加便捷的写法。只需要创建一个类,然后再对应字段上打上 @ExcelExport 注解类即可。

    Controller 代码:

    1. @GetMapping("/export")
    2. public void export(HttpServletResponse response) {
    3. ExcelUtils.exportTemplate(response, "用户表", User.class);
    4. }

    代码截图:

    测试效果:

    1.3.7 导出模板(附示例数据)

    我们在做模版下载时候,有时往往会携带一条样本数据,好提示用户数据格式是什么,那么我们只需要在对应字段上进行配置即可。

    Controller代码:

    测试效果:

    1.3.8 按对象导出(基础)

    我们还可以通过 List 对象,对数据直接进行导出。首先,同样需要在对应类的字段上,设置导出名称。

    Controller 代码:

    测试效果:

    1.3.9 按对象导出(数据映射)

    在上面 1.3.8 的导出中,我们可以看到,性别数据导出来是1和2,这个不利于用户体验,应该需要转换为对应的中文,我们可以在字段注解上进行对应的配置。

    Controller 代码略(和1.3.8完全一致)

    测试效果:可以看到1和2显示为了对应的男和女

    1.3.10 按对象导出(调整表头顺序)

    如果你需要对表头字段进行排序,有两种方式:

    第一种:按照表格的顺序,排列Java类中的字段;

    第二种:在 @ExcelExport 注解中,指定 sort 属性,其值越少,排名越靠前。

    Controller 代码略(和1.3.8完全一致)

    测试效果:可以看到,此时导出数据的表头顺序,和我们指定的顺序完全一致。

    2. 环境准备

    2.1 Maven 依赖

    本次工具类的封装主要依赖于阿里巴巴的JSON包,以及表格处理的POI包,所以我们需要导入这两个库的依赖包,另外,我们还需要文件上传的相关包,毕竟我们在浏览器页面,做Excel导入时,是上传的Excel文件。

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

    2.2 类文件

    ExcelUtils

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

    ExcelImport

    1. package com.zyq.util.excel;
    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. /**
    7. * @author sunnyzyq
    8. * @date 2021/12/17
    9. */
    10. @Target(ElementType.FIELD)
    11. @Retention(RetentionPolicy.RUNTIME)
    12. public @interface ExcelImport {
    13. /** 字段名称 */
    14. String value();
    15. /** 导出映射,格式如:0-未知;1-男;2-女 */
    16. String kv() default "";
    17. /** 是否为必填字段(默认为非必填) */
    18. boolean required() default false;
    19. /** 最大长度(默认255*/
    20. int maxLength() default 255;
    21. /** 导入唯一性验证(多个字段则取联合验证) */
    22. boolean unique() default false;
    23. }

    ExcelExport

    1. package com.zyq.util.excel;
    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. /**
    7. * @author sunnyzyq
    8. * @date 2021/12/17
    9. */
    10. @Target(ElementType.FIELD)
    11. @Retention(RetentionPolicy.RUNTIME)
    12. public @interface ExcelExport {
    13. /** 字段名称 */
    14. String value();
    15. /** 导出排序先后: 数字越小越靠前(默认按Java类字段顺序导出) */
    16. int sort() default 0;
    17. /** 导出映射,格式如:0-未知;1-男;2-女 */
    18. String kv() default "";
    19. /** 导出模板示例值(有值的话,直接取该值,不做映射) */
    20. String example() default "";
    21. }

    ExcelClassField

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

    好了, 以上是本文所有内容,希望对大家有所帮助,也希望大家对码农之家多多支持,你们的支持是我创作的动力!祝大家生活愉快!   

  • 相关阅读:
    [开源]一款面向普通人的AI桌面APP工具箱简单方便使用
    Windows socket测试工具
    Balanced Multimodal Learning via On-the-fly Gradient Modulation(CVPR2022 oral)
    Java 复习笔记 - 字符串篇
    41_ue4进阶末日生存游戏开发[判断左右键点击]
    从零开始搭建一个微服务项目(一)
    物业一站式工单管理系统哪家好?如何提升物业管理和维修服务质量?
    TypeScript(6)函数
    【Hack The Box】linux练习-- Delivery
    【前端设计模式】之单例模式
  • 原文地址:https://blog.csdn.net/wuxiaopengnihao1/article/details/126686520