Java POI实现Excel导入-通用组件

谁借莪1个温暖的怀抱¢ 2022-03-28 04:09 353阅读 0赞

在工作中,会经常大量使用到Excel导入,如果每个开发人员都自己实现一套Excel导入,带来的问题是研发效率降低,项目中存在大量重复的代码,这时就有必要将Excel导入写成通用组件,开发人员使用该组件,便可轻松实现导入功能
设计思路:
1.将Excel导入设计成模板形式,导入前需要配置此模板
2.通过反射的形式调用对应VO的属性的set方法,给属性赋值
3.返回解析后的结果集

Excel导入模板设计
在src/main/resource/excel/目录下,创建mairuan.import.template.xml文件,将要导入的Excel,配置成模板的形式,内容如下

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <import>
  3. <!-- type类型:Byte,Short,Integer,Long,Float,Double,char,String,缺省为String -->
  4. <sheet id="importRole" startRow="1" class="com.mairuan.base.system.model.RoleVO">
  5. <column name="RoleCode" type="String"></column>
  6. <column name="RoleName" type="String"></column>
  7. <column name="RoleDesc" type="String"></column>
  8. <column name="RoleState" type="String"></column>
  9. </sheet>
  10. </import>

说明:
1.一个sheet标签代表一个sheet页
2.startRow代表从哪行开始解析
3.class代表使用哪个对象来承载Excel中的值
4.name 对应class对应中的属性名,首字母必须大写
5.type表示该字段的数据类型,解析Excel时,得到单元格中的数据类型,和我们预想的不一至,会影响通过反射调用set方法,因此使用该字段进行类型转换

创建ImportExcelUtil工具类
1. 在工具类中增加解析mairuan.import.template.xml模板的方法

  1. @SuppressWarnings("unchecked")
  2. private static Element readXML(String sheetId) throws DocumentException, FileNotFoundException {
  3. File file = ResourceUtils.getFile("classpath:/excel/mairuan.import.template.xml");
  4. SAXReader reader = new SAXReader();
  5. Document document = reader.read(file);
  6. Element root = document.getRootElement();
  7. Iterator<Element> iterator = root.elementIterator();
  8. Element element = null;
  9. while (iterator.hasNext()) {
  10. element = iterator.next();
  11. if (sheetId.equals(element.attributeValue("id"))) {
  12. break;
  13. }
  14. }
  15. return element;
  16. }

根据传入的sheetID,查找对应的标签,如果找到,就反回

2. 在工具类中增加根据type值获得参数类型的方法

  1. private static Class<?> getParameterType(String parameterType) {
  2. switch (parameterType) {
  3. case "Byte":
  4. return Byte.class;
  5. case "Short":
  6. return Short.class;
  7. case "Integer":
  8. return Integer.class;
  9. case "Long":
  10. return Long.class;
  11. case "Float":
  12. return Float.class;
  13. case "Double":
  14. return Double.class;
  15. case "char":
  16. return char.class;
  17. case "String":
  18. return String.class;
  19. case "Date":
  20. return Date.class;
  21. default:
  22. return String.class;
  23. }
  24. }

3. 在工具类中增加解析Excel的主方法

  1. @SuppressWarnings("unchecked")
  2. public static List parseExcel(String sheetId, InputStream input) throws Exception {
  3. // 根据sheetId解析xml,获取Excel模板
  4. Element element = readXML(sheetId);
  5. String clazzString = element.attributeValue("class");
  6. // 从哪行开始解析
  7. int start = Integer.valueOf(element.attributeValue("startRow"));
  8. // 反射创建对象实例
  9. Class<? extends Object> clazz = Class.forName(clazzString);
  10. List<Element> columns = element.elements();
  11. Object instance = null;
  12. // 结果集
  13. List resultList = new ArrayList();
  14. // 要调用的方法
  15. Method method = null;
  16. // 调用Set方法要传递的参数
  17. Class<?>[] args = null;
  18. Cell cell = null;
  19. XSSFWorkbook wb = null;
  20. try
  21. {
  22. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  23. // wb = new XSSFWorkbook(file);
  24. wb = new XSSFWorkbook(input);
  25. // 获取第一个Sheet页
  26. XSSFSheet sheet = wb.getSheetAt(0);
  27. for (int i = start; i <= sheet.getLastRowNum(); i++) {
  28. Row row = sheet.getRow(i);
  29. if(row == null)
  30. {
  31. continue;
  32. }
  33. instance = clazz.newInstance();
  34. StringBuffer buf = new StringBuffer();
  35. for (int j = 0; j < row.getLastCellNum(); j++) {
  36. cell = row.getCell(j);
  37. if (cell == null) {
  38. continue;
  39. }
  40. args = new Class[1];
  41. args[0] = getParameterType(columns.get(j).attributeValue("type"));
  42. Object value = getCellValue(cell,args[0],dateFormat);
  43. //累计字符串内容
  44. buf.append(getStringValue(value));
  45. //Date情况转成String,其他类型保持原样
  46. args[0] = Date.class.equals(args[0]) ? String.class : args[0];
  47. method = clazz.getDeclaredMethod("set" + columns.get(j).attributeValue("name"), args);
  48. method.invoke(instance, value);
  49. }
  50. //整行为空时丢弃该行数据
  51. if ("".equals(buf.toString().trim()))
  52. {
  53. continue;
  54. }
  55. //加到集合中
  56. resultList.add(instance);
  57. }
  58. } catch (Exception e) {
  59. throw e;
  60. } finally {
  61. if (wb != null) {
  62. wb.close();
  63. }
  64. }
  65. return resultList;
  66. }

说明:
1.外层for循环是读取Excel中的行,内层循环是解析每一行数据到对应的VO属性中
2.通过反射的方式给VO属性赋值
3.根据getCellValue方法获得不同类型的数据值,如果类型不对,反射执行赋值会失败
4.如果整行为空,则丢弃这行记录
5.最后将结果集resultList返回

最后完整的ImportExcelUtil工具类如下

  1. package com.mairuan.base.utils;
  2. import java.io.File;
  3. import java.io.FileNotFoundException;
  4. import java.io.InputStream;
  5. import java.lang.reflect.Method;
  6. import java.text.DecimalFormat;
  7. import java.text.SimpleDateFormat;
  8. import java.util.ArrayList;
  9. import java.util.Date;
  10. import java.util.Iterator;
  11. import java.util.List;
  12. import org.apache.poi.ss.usermodel.Cell;
  13. import org.apache.poi.ss.usermodel.Row;
  14. import org.apache.poi.xssf.usermodel.XSSFSheet;
  15. import org.apache.poi.xssf.usermodel.XSSFWorkbook;
  16. import org.dom4j.Document;
  17. import org.dom4j.DocumentException;
  18. import org.dom4j.Element;
  19. import org.dom4j.io.SAXReader;
  20. import org.springframework.util.ResourceUtils;
  21. public class ImportExcelUtil {
  22. @SuppressWarnings("unchecked")
  23. public static List parseExcel(String sheetId, InputStream input) throws Exception {
  24. // 根据sheetId解析xml,获取Excel模板
  25. Element element = readXML(sheetId);
  26. String clazzString = element.attributeValue("class");
  27. // 从哪行开始解析
  28. int start = Integer.valueOf(element.attributeValue("startRow"));
  29. // 反射创建对象实例
  30. Class<? extends Object> clazz = Class.forName(clazzString);
  31. List<Element> columns = element.elements();
  32. Object instance = null;
  33. // 结果集
  34. List resultList = new ArrayList();
  35. // 要调用的方法
  36. Method method = null;
  37. // 调用Set方法要传递的参数
  38. Class<?>[] args = null;
  39. Cell cell = null;
  40. XSSFWorkbook wb = null;
  41. try
  42. {
  43. SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
  44. // wb = new XSSFWorkbook(file);
  45. wb = new XSSFWorkbook(input);
  46. // 获取第一个Sheet页
  47. XSSFSheet sheet = wb.getSheetAt(0);
  48. for (int i = start; i <= sheet.getLastRowNum(); i++) {
  49. Row row = sheet.getRow(i);
  50. if(row == null)
  51. {
  52. continue;
  53. }
  54. instance = clazz.newInstance();
  55. StringBuffer buf = new StringBuffer();
  56. for (int j = 0; j < row.getLastCellNum(); j++) {
  57. cell = row.getCell(j);
  58. if (cell == null) {
  59. continue;
  60. }
  61. args = new Class[1];
  62. args[0] = getParameterType(columns.get(j).attributeValue("type"));
  63. Object value = getCellValue(cell,args[0],dateFormat);
  64. //累计字符串内容
  65. buf.append(getStringValue(value));
  66. //Date情况转成String,其他类型保持原样
  67. args[0] = Date.class.equals(args[0]) ? String.class : args[0];
  68. method = clazz.getDeclaredMethod("set" + columns.get(j).attributeValue("name"), args);
  69. method.invoke(instance, value);
  70. }
  71. //整行为空时丢弃该行数据
  72. if ("".equals(buf.toString().trim()))
  73. {
  74. continue;
  75. }
  76. //加到集合中
  77. resultList.add(instance);
  78. }
  79. } catch (Exception e) {
  80. throw e;
  81. } finally {
  82. if (wb != null) {
  83. wb.close();
  84. }
  85. }
  86. return resultList;
  87. }
  88. public static boolean checkFileType(String filename) {
  89. if ((filename == null) || (filename.length() == 0)) {
  90. return false;
  91. }
  92. String type = "";
  93. int dot = filename.lastIndexOf('.');
  94. if ((dot > -1) && (dot < (filename.length() - 1))) {
  95. type = filename.substring(dot + 1);
  96. }
  97. if (!"xlsx".equalsIgnoreCase(type)) {
  98. return false;
  99. }
  100. return true;
  101. }
  102. @SuppressWarnings("unchecked")
  103. private static Element readXML(String sheetId) throws DocumentException, FileNotFoundException {
  104. File file = ResourceUtils.getFile("classpath:/excel/mcc.base.import.xml");
  105. SAXReader reader = new SAXReader();
  106. Document document = reader.read(file);
  107. Element root = document.getRootElement();
  108. Iterator<Element> iterator = root.elementIterator();
  109. Element element = null;
  110. while (iterator.hasNext()) {
  111. element = iterator.next();
  112. if (sheetId.equals(element.attributeValue("id"))) {
  113. break;
  114. }
  115. }
  116. return element;
  117. }
  118. private static Object getCellValue(Cell cell,Class<? extends Object> clazz,SimpleDateFormat dateFormat) {
  119. Object obj = null;
  120. switch (cell.getCellType()) {
  121. case Cell.CELL_TYPE_BOOLEAN:
  122. obj = cell.getBooleanCellValue();
  123. break;
  124. case Cell.CELL_TYPE_ERROR:
  125. obj = cell.getErrorCellValue();
  126. break;
  127. case Cell.CELL_TYPE_NUMERIC:
  128. //用户自定义类型检测
  129. if(String.class.equals(clazz))
  130. {
  131. DecimalFormat df = new DecimalFormat("#.#########"); // 数字格式,防止长数字成为科学计数
  132. obj = df.format(cell.getNumericCellValue());
  133. }else if(Date.class.equals(clazz))
  134. {
  135. obj = dateFormat.format(cell.getDateCellValue());
  136. }else if(Double.class.equals(clazz)) {
  137. obj = cell.getNumericCellValue();
  138. }else if(Integer.class.equals(clazz)) {
  139. DecimalFormat df = new DecimalFormat("#.#########"); // 数字格式,防止长数字成为科学计数
  140. String val = df.format(cell.getNumericCellValue());
  141. obj = Integer.parseInt(val);
  142. }
  143. break;
  144. case Cell.CELL_TYPE_STRING:
  145. String cellValue = cell.getStringCellValue();
  146. if (Double.class.equals(clazz)) {
  147. Double d = Double.valueOf(cellValue);
  148. obj = d;
  149. }else if(Integer.class.equals(clazz)) {
  150. obj = Integer.parseInt(cellValue);
  151. }else {
  152. obj=cellValue;
  153. }
  154. break;
  155. default:
  156. break;
  157. }
  158. return obj;
  159. }
  160. /**
  161. * 获取参数类型,默认为String
  162. *
  163. * @param parameterType
  164. * @return
  165. */
  166. private static Class<?> getParameterType(String parameterType) {
  167. switch (parameterType) {
  168. case "Byte":
  169. return Byte.class;
  170. case "Short":
  171. return Short.class;
  172. case "Integer":
  173. return Integer.class;
  174. case "Long":
  175. return Long.class;
  176. case "Float":
  177. return Float.class;
  178. case "Double":
  179. return Double.class;
  180. case "char":
  181. return char.class;
  182. case "String":
  183. return String.class;
  184. case "Date":
  185. return Date.class;
  186. default:
  187. return String.class;
  188. }
  189. }
  190. /**
  191. * 获取字符串内容
  192. * @param obj
  193. * @return
  194. */
  195. private static String getStringValue(Object obj)
  196. {
  197. if (obj != null)
  198. {
  199. return String.valueOf(obj);
  200. }
  201. return "";
  202. }
  203. }

Spring MVC控制层调用导入工具类解析Excel

  1. @RequestMapping(value = "/importCoreList", method = RequestMethod.POST)
  2. @ResponseBody
  3. public ResponseVO<List<RoleVO>> importExcel(HttpServletRequest request, @RequestParam("file") MultipartFile file)
  4. throws Exception {
  5. ResponseVO<List<RoleVO>> response = new ResponseVO<List<RoleVO>>();
  6. try {
  7. if (file.isEmpty()) {
  8. response.setStatus(ResponseVO.failCode);
  9. response.setMessage("文件为空");
  10. return response;
  11. }
  12. if (!ImportExcelUtil.checkFileType(file.getOriginalFilename())) {
  13. response.setStatus(ResponseVO.failCode);
  14. response.setMessage("只支持2007版以上的Excel导入");
  15. return response;
  16. }
  17. String sheetId = "importRole";
  18. List<RoleVO> result = (List<RoleVO>) ImportExcelUtil.parseExcel(sheetId, file.getInputStream());
  19. response.setData(result );
  20. } catch (Exception e) {
  21. logger.error("Import Excel fail,The message is:", e);
  22. response.setStatus(ResponseVO.failCode);
  23. }
  24. return response;
  25. }

开发人员只需要通过List result = (List) ImportExcelUtil.parseExcel(sheetId, file.getInputStream());就可以将Excel中的值转换成想要的VO对应,进行后面的数据操作

发表评论

表情:
评论列表 (有 0 条评论,353人围观)

还没有评论,来说两句吧...

相关阅读