基于Java编写一个实用的ExcelUtil工具类
场景:在项目中经常遇到excel表格导入导出功能,每次都要重复写有关excel 的逻辑,在网上找了一圈没有找到自己想要的,于是我就自己创建一个工具类,实现了简单的导入和导出功能。
一、首先引入 Maven 依赖
<!-- java poi --> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency> <!--支持xlsx读取--> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>3.17</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml-schemas</artifactId> <version>3.17</version> </dependency>
二、创建 ExcelUtil 工具类
创建了工具类,我们要实现的有导入
和导出
两个主要功能
实现导入功能
导入功能实现要考虑几点:
- Excel表格中的每一列如何和Java实体类中的属性关联。
- 表头占几行是否需要去掉。
- 多个 sheet 页是否都要导入。
我的代码如下:
/** * * @param file 导入的excel 文件 * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名 * @param excelTitleToPojoMap 列名对应java 中实体类属性名 * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页 * @param startRow 从第几行开始导入数据 * @param clazz list 列表中的元素类型 */ public static <T> List<T> importExcelByFile(MultipartFile file, Map<Integer, String> excelRowToTitleMap, Map<String, String> excelTitleToPojoMap, List<String> sheetList, Integer startRow, Class<T> clazz) { List<T> list = new ArrayList<>(); try (InputStream fis = file.getInputStream()){ list = importExcel(fis, excelRowToTitleMap, excelTitleToPojoMap, sheetList, startRow, clazz); } catch (Exception e) { log.info("import excel exception:{}", e.getMessage()); } return list; } /** * * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名 * @param excelTitleToPojoMap 列名对应java 中实体类属性名 * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页 * @param startRow 从第几行开始导入数据 * @param clazz list 列表中的元素类型 */ public static <T> List<T> importExcel(InputStream inputStream, Map<Integer, String> excelRowToTitleMap, Map<String, String> excelTitleToPojoMap, List<String> sheetList, Integer startRow, Class<T> clazz) { Set<Integer> rows = excelRowToTitleMap.keySet(); List<T> list = new ArrayList<>(); try (Workbook workbook = WorkbookFactory.create(inputStream)) { for (Sheet sheet : workbook) { String sheetName = sheet.getSheetName(); // 如果指定了要导入的 sheet 页,就需要过滤其他的 if (sheetList != null && !sheetList.contains(sheetName)) { continue; } for (Row row : sheet) { // 去掉表头 if (row.getRowNum() + 1 < startRow) { continue; } // 反射获取对象 T obj = clazz.newInstance(); for (int i = 0; i < row.getLastCellNum(); i++) { Cell cell = row.getCell(i); String cellValue = cell != null ? cell.toString() : null; if (rows.contains(i + 1)) { String rowTitleName = excelRowToTitleMap.get(i + 1); String propertyName = excelTitleToPojoMap.get(rowTitleName); if (propertyName != null) { // 反射获取字段的set方法 PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyName, clazz); Method writeMethod = propertyDescriptor.getWriteMethod(); if (writeMethod != null) { Class<?> propertyType = propertyDescriptor.getPropertyType(); Object value = convertValue(cellValue, propertyType); writeMethod.invoke(obj, value); } } } } list.add(obj); } } } catch (Exception e) { log.info("import excel exception:{}", e.getMessage()); } return list; }
上面代码中excelRowToTitleMap和excelTitleToPojoMap解决了Excel表格中的每一列如何和Java实体类中的属性关联关系。sheetList 列出了需要导入的 sheet 页,startRow 则是要从第几行读取数据。这样上面的几个问题就都解决了。
实现导出功能
项目中的常见的就是 list 导出到 excel。
同样的要考虑几个问题:
- excel 表头可以自定义中文名。
- 可以指定导出哪些字段。
我的代码如下:
/** * 将 list 导入 excel * @param list list 数据列表 * @param obj list 的实体对象 * @param header 表头数组 (为 null 时,实体类中属性名作为表头) * @param rows 导出的列 (为 null 导出所有列) * @param fileName 文件名称 * @param response response */ public static void exportListToExcel(List<?> list, Class obj, String[] header, String[] rows, String fileName, HttpServletResponse response) { try { //1.创建工作簿 Workbook book = new XSSFWorkbook(); //2.创建 sheet 页 Sheet sheet = book.createSheet(); //获取 get 方法 List<Method> methods = new LinkedList<>(); if (rows == null || rows.length == 0) { //获取实体类中所有列 get 方法 BeanInfo beanInfo = Introspector.getBeanInfo(obj); MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); for (int i = 0; i < methodDescriptors.length; i++) { String method = methodDescriptors[i].getName(); if (Pattern.matches("[g][e][t]\\w*",method) && !"getClass".equals(method)) { methods.add(methodDescriptors[i].getMethod()); } } } else { //获取指定列的 get 方法 for (int i = 0; i < rows.length; i++) { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(rows[i], obj); methods.add(propertyDescriptor.getReadMethod()); } } if (header == null || header.length == 0) { header = new String[methods.size()]; for (int i = 0; i < header.length; i++) { String methodName = methods.get(i).getName(); header[i] = methodName.substring(3,4).toLowerCase() + methodName.substring(4); } } //3.创建表头 Row headerRow = sheet.createRow(0); CellStyle headerStyle = getHeaderStyle(book); setHeaderRow(header, headerRow, headerStyle); //4.填充数据 int sheetFlag = 0; if (!CollectionUtils.isEmpty(list)) { for (int i = 0; i < list.size(); i++) { //超过最大行数,自动创建下一页 if ((i+1)%1048575 == 0) { sheet = book.createSheet("sheet" + sheetFlag); headerRow = sheet.createRow(0); setHeaderRow(header, headerRow, headerStyle); sheetFlag ++; } //getLastRowNum 获取行标(表头行标为 0) Row row = sheet.createRow(sheet.getLastRowNum() + 1); Object o = list.get(i); for (int j = 0; j < methods.size(); j++) { Object value = methods.get(j).invoke(o); row.createCell(j).setCellValue(value != null ? value.toString() : "-"); } } } //5.将 excel 表格导出 response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename="+new String(fileName.getBytes(StandardCharsets.UTF_8),"ISO8859-1") + ".xlsx"); ServletOutputStream outputStream = response.getOutputStream(); book.write(outputStream); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
我通过header和rows两个参数可以将excel 列表和实体类中的属性关联起来(两个数组中的元素位置要一一对应),然后通过反射获取到list列表元素写入到excel 中。
至此基础的导入和导出功能就ok了
完整的代码
package com.base.utils; import lombok.extern.slf4j.Slf4j; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.springframework.util.CollectionUtils; import org.springframework.web.multipart.MultipartFile; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import java.beans.BeanInfo; import java.beans.Introspector; import java.beans.MethodDescriptor; import java.beans.PropertyDescriptor; import java.io.InputStream; import java.lang.reflect.Method; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.regex.Pattern; @Slf4j public class ExcelUtil { /** * * @param file 导入的excel 文件 * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名 * @param excelTitleToPojoMap 列名对应java 中实体类属性名 * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页 * @param startRow 从第几行开始导入数据 * @param clazz list 列表中的元素类型 */ public static <T> List<T> importExcelByFile(MultipartFile file, Map<Integer, String> excelRowToTitleMap, Map<String, String> excelTitleToPojoMap, List<String> sheetList, Integer startRow, Class<T> clazz) { List<T> list = new ArrayList<>(); try (InputStream fis = file.getInputStream()){ list = importExcel(fis, excelRowToTitleMap, excelTitleToPojoMap, sheetList, startRow, clazz); } catch (Exception e) { log.info("import excel exception:{}", e.getMessage()); } return list; } /** * * @param excelRowToTitleMap excel 的表头对应关系, key 为第几列(从 1 开始), value 为 列名 * @param excelTitleToPojoMap 列名对应java 中实体类属性名 * @param sheetList 要导入的 sheetList 的列表,不传默认全部的 sheet 页 * @param startRow 从第几行开始导入数据 * @param clazz list 列表中的元素类型 */ public static <T> List<T> importExcel(InputStream inputStream, Map<Integer, String> excelRowToTitleMap, Map<String, String> excelTitleToPojoMap, List<String> sheetList, Integer startRow, Class<T> clazz) { Set<Integer> rows = excelRowToTitleMap.keySet(); List<T> list = new ArrayList<>(); try (Workbook workbook = WorkbookFactory.create(inputStream)) { for (Sheet sheet : workbook) { String sheetName = sheet.getSheetName(); // 如果指定了要导入的 sheet 页,就需要过滤其他的 if (sheetList != null && !sheetList.contains(sheetName)) { continue; } for (Row row : sheet) { // 去掉表头 if (row.getRowNum() + 1 < startRow) { continue; } // 反射获取对象 T obj = clazz.newInstance(); for (int i = 0; i < row.getLastCellNum(); i++) { Cell cell = row.getCell(i); String cellValue = cell != null ? cell.toString() : null; if (rows.contains(i + 1)) { String rowTitleName = excelRowToTitleMap.get(i + 1); String propertyName = excelTitleToPojoMap.get(rowTitleName); if (propertyName != null) { // 反射获取字段的set方法 PropertyDescriptor propertyDescriptor = new PropertyDescriptor(propertyName, clazz); Method writeMethod = propertyDescriptor.getWriteMethod(); if (writeMethod != null) { Class<?> propertyType = propertyDescriptor.getPropertyType(); Object value = convertValue(cellValue, propertyType); writeMethod.invoke(obj, value); } } } } list.add(obj); } } } catch (Exception e) { log.info("import excel exception:{}", e.getMessage()); } return list; } /** * 将 list 导入 excel * @param list list 数据列表 * @param obj list 的实体对象 * @param header 表头数组 (为 null 时,实体类中属性名作为表头) * @param rows 导出的列 (为 null 导出所有列) * @param fileName 文件名称 * @param response response */ public static void exportListToExcel(List<?> list, Class obj, String[] header, String[] rows, String fileName, HttpServletResponse response) { try { //1.创建工作簿 Workbook book = new XSSFWorkbook(); //2.创建 sheet 页 Sheet sheet = book.createSheet(); //获取 get 方法 List<Method> methods = new LinkedList<>(); if (rows == null || rows.length == 0) { //获取实体类中所有列 get 方法 BeanInfo beanInfo = Introspector.getBeanInfo(obj); MethodDescriptor[] methodDescriptors = beanInfo.getMethodDescriptors(); for (int i = 0; i < methodDescriptors.length; i++) { String method = methodDescriptors[i].getName(); if (Pattern.matches("[g][e][t]\\w*",method) && !"getClass".equals(method)) { methods.add(methodDescriptors[i].getMethod()); } } } else { //获取指定列的 get 方法 for (int i = 0; i < rows.length; i++) { PropertyDescriptor propertyDescriptor = new PropertyDescriptor(rows[i], obj); methods.add(propertyDescriptor.getReadMethod()); } } if (header == null || header.length == 0) { header = new String[methods.size()]; for (int i = 0; i < header.length; i++) { String methodName = methods.get(i).getName(); header[i] = methodName.substring(3,4).toLowerCase() + methodName.substring(4); } } //3.创建表头 Row headerRow = sheet.createRow(0); CellStyle headerStyle = getHeaderStyle(book); setHeaderRow(header, headerRow, headerStyle); //4.填充数据 int sheetFlag = 0; if (!CollectionUtils.isEmpty(list)) { for (int i = 0; i < list.size(); i++) { //超过最大行数,自动创建下一页 if ((i+1)%1048575 == 0) { sheet = book.createSheet("sheet" + sheetFlag); headerRow = sheet.createRow(0); setHeaderRow(header, headerRow, headerStyle); sheetFlag ++; } //getLastRowNum 获取行标(表头行标为 0) Row row = sheet.createRow(sheet.getLastRowNum() + 1); Object o = list.get(i); for (int j = 0; j < methods.size(); j++) { Object value = methods.get(j).invoke(o); row.createCell(j).setCellValue(value != null ? value.toString() : "-"); } } } //5.将 excel 表格导出 response.setContentType("application/vnd.ms-excel;charset=utf-8"); response.setCharacterEncoding("utf-8"); response.setHeader("Content-Disposition", "attachment;filename="+new String(fileName.getBytes(StandardCharsets.UTF_8),"ISO8859-1") + ".xlsx"); ServletOutputStream outputStream = response.getOutputStream(); book.write(outputStream); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } } private static Object convertValue(String cellValue, Class<?> targetType) { if (cellValue == null || targetType == null) { return null; } if (String.class.equals(targetType)) { return cellValue; } else if (Integer.class.equals(targetType) || int.class.equals(targetType)) { return Integer.valueOf(cellValue); } else if (Double.class.equals(targetType) || double.class.equals(targetType)) { return Double.valueOf(cellValue); } else if (Boolean.class.equals(targetType) || boolean.class.equals(targetType)) { return Boolean.valueOf(cellValue); } // 可以根据需要添加其他类型的转换 return cellValue; } /** * 设置表头 * @param header * @param headerRow * @param headerStyle */ private static void setHeaderRow(String[] header, Row headerRow, CellStyle headerStyle) { for (int i = 0; i < header.length; i++) { Cell headerCell = headerRow.createCell(i); headerCell.setCellValue(header[i]); headerCell.setCellStyle(headerStyle); } } /** * 表头样式 * @param book */ private static CellStyle getHeaderStyle(Workbook book) { CellStyle headerStyle = book.createCellStyle(); Font headerFont = book.createFont(); headerFont.setBold(true); //设置粗体 headerFont.setFontName("表头加粗字体"); headerStyle.setFont(headerFont); return headerStyle; } }
以上就是基于Java编写一个实用的ExcelUtil工具类的详细内容,更多关于Java ExcelUtil工具类的资料请关注脚本之家其它相关文章!
相关文章
SSH框架网上商城项目第19战之订单信息级联入库以及页面缓存问题
这篇文章主要介绍了SSH框架网上商城项目第19战之订单信息级联入库以及页面缓存问题,感兴趣的小伙伴们可以参考一下2016-06-06
最新评论