基于Java編寫一個(gè)實(shí)用的ExcelUtil工具類
場(chǎng)景:在項(xiàng)目中經(jīng)常遇到excel表格導(dǎo)入導(dǎo)出功能,每次都要重復(fù)寫有關(guān)excel 的邏輯,在網(wǎng)上找了一圈沒有找到自己想要的,于是我就自己創(chuàng)建一個(gè)工具類,實(shí)現(xiàn)了簡(jiǎn)單的導(dǎo)入和導(dǎo)出功能。
一、首先引入 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>
二、創(chuàng)建 ExcelUtil 工具類
創(chuàng)建了工具類,我們要實(shí)現(xiàn)的有導(dǎo)入
和導(dǎo)出
兩個(gè)主要功能
實(shí)現(xiàn)導(dǎo)入功能
導(dǎo)入功能實(shí)現(xiàn)要考慮幾點(diǎn):
- Excel表格中的每一列如何和Java實(shí)體類中的屬性關(guān)聯(lián)。
- 表頭占幾行是否需要去掉。
- 多個(gè) sheet 頁(yè)是否都要導(dǎo)入。
我的代碼如下:
/** * * @param file 導(dǎo)入的excel 文件 * @param excelRowToTitleMap excel 的表頭對(duì)應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名 * @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類屬性名 * @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè) * @param startRow 從第幾行開始導(dǎo)入數(shù)據(jù) * @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 的表頭對(duì)應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名 * @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類屬性名 * @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè) * @param startRow 從第幾行開始導(dǎo)入數(shù)據(jù) * @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(); // 如果指定了要導(dǎo)入的 sheet 頁(yè),就需要過濾其他的 if (sheetList != null && !sheetList.contains(sheetName)) { continue; } for (Row row : sheet) { // 去掉表頭 if (row.getRowNum() + 1 < startRow) { continue; } // 反射獲取對(duì)象 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實(shí)體類中的屬性關(guān)聯(lián)關(guān)系。sheetList 列出了需要導(dǎo)入的 sheet 頁(yè),startRow 則是要從第幾行讀取數(shù)據(jù)。這樣上面的幾個(gè)問題就都解決了。
實(shí)現(xiàn)導(dǎo)出功能
項(xiàng)目中的常見的就是 list 導(dǎo)出到 excel。
同樣的要考慮幾個(gè)問題:
- excel 表頭可以自定義中文名。
- 可以指定導(dǎo)出哪些字段。
我的代碼如下:
/** * 將 list 導(dǎo)入 excel * @param list list 數(shù)據(jù)列表 * @param obj list 的實(shí)體對(duì)象 * @param header 表頭數(shù)組 (為 null 時(shí),實(shí)體類中屬性名作為表頭) * @param rows 導(dǎo)出的列 (為 null 導(dǎo)出所有列) * @param fileName 文件名稱 * @param response response */ public static void exportListToExcel(List<?> list, Class obj, String[] header, String[] rows, String fileName, HttpServletResponse response) { try { //1.創(chuàng)建工作簿 Workbook book = new XSSFWorkbook(); //2.創(chuàng)建 sheet 頁(yè) Sheet sheet = book.createSheet(); //獲取 get 方法 List<Method> methods = new LinkedList<>(); if (rows == null || rows.length == 0) { //獲取實(shí)體類中所有列 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.創(chuàng)建表頭 Row headerRow = sheet.createRow(0); CellStyle headerStyle = getHeaderStyle(book); setHeaderRow(header, headerRow, headerStyle); //4.填充數(shù)據(jù) int sheetFlag = 0; if (!CollectionUtils.isEmpty(list)) { for (int i = 0; i < list.size(); i++) { //超過最大行數(shù),自動(dòng)創(chuàng)建下一頁(yè) if ((i+1)%1048575 == 0) { sheet = book.createSheet("sheet" + sheetFlag); headerRow = sheet.createRow(0); setHeaderRow(header, headerRow, headerStyle); sheetFlag ++; } //getLastRowNum 獲取行標(biāo)(表頭行標(biāo)為 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 表格導(dǎo)出 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兩個(gè)參數(shù)可以將excel 列表和實(shí)體類中的屬性關(guān)聯(lián)起來(lái)(兩個(gè)數(shù)組中的元素位置要一一對(duì)應(yīng)),然后通過反射獲取到list列表元素寫入到excel 中。
至此基礎(chǔ)的導(dǎo)入和導(dǎo)出功能就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 導(dǎo)入的excel 文件 * @param excelRowToTitleMap excel 的表頭對(duì)應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名 * @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類屬性名 * @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè) * @param startRow 從第幾行開始導(dǎo)入數(shù)據(jù) * @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 的表頭對(duì)應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名 * @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類屬性名 * @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè) * @param startRow 從第幾行開始導(dǎo)入數(shù)據(jù) * @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(); // 如果指定了要導(dǎo)入的 sheet 頁(yè),就需要過濾其他的 if (sheetList != null && !sheetList.contains(sheetName)) { continue; } for (Row row : sheet) { // 去掉表頭 if (row.getRowNum() + 1 < startRow) { continue; } // 反射獲取對(duì)象 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 導(dǎo)入 excel * @param list list 數(shù)據(jù)列表 * @param obj list 的實(shí)體對(duì)象 * @param header 表頭數(shù)組 (為 null 時(shí),實(shí)體類中屬性名作為表頭) * @param rows 導(dǎo)出的列 (為 null 導(dǎo)出所有列) * @param fileName 文件名稱 * @param response response */ public static void exportListToExcel(List<?> list, Class obj, String[] header, String[] rows, String fileName, HttpServletResponse response) { try { //1.創(chuàng)建工作簿 Workbook book = new XSSFWorkbook(); //2.創(chuàng)建 sheet 頁(yè) Sheet sheet = book.createSheet(); //獲取 get 方法 List<Method> methods = new LinkedList<>(); if (rows == null || rows.length == 0) { //獲取實(shí)體類中所有列 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.創(chuàng)建表頭 Row headerRow = sheet.createRow(0); CellStyle headerStyle = getHeaderStyle(book); setHeaderRow(header, headerRow, headerStyle); //4.填充數(shù)據(jù) int sheetFlag = 0; if (!CollectionUtils.isEmpty(list)) { for (int i = 0; i < list.size(); i++) { //超過最大行數(shù),自動(dòng)創(chuàng)建下一頁(yè) if ((i+1)%1048575 == 0) { sheet = book.createSheet("sheet" + sheetFlag); headerRow = sheet.createRow(0); setHeaderRow(header, headerRow, headerStyle); sheetFlag ++; } //getLastRowNum 獲取行標(biāo)(表頭行標(biāo)為 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 表格導(dǎo)出 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); } // 可以根據(jù)需要添加其他類型的轉(zhuǎn)換 return cellValue; } /** * 設(shè)置表頭 * @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); //設(shè)置粗體 headerFont.setFontName("表頭加粗字體"); headerStyle.setFont(headerFont); return headerStyle; } }
以上就是基于Java編寫一個(gè)實(shí)用的ExcelUtil工具類的詳細(xì)內(nèi)容,更多關(guān)于Java ExcelUtil工具類的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IDEA關(guān)于.properties資源文件的編碼調(diào)整問題
這篇文章主要介紹了IDEA關(guān)于.properties資源文件的編碼調(diào)整問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-06-06SpringBoot響應(yīng)Json數(shù)據(jù)亂碼通過配置的解決
這篇文章主要介紹了SpringBoot響應(yīng)Json數(shù)據(jù)亂碼通過配置的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11