基于Java編寫一個實用的ExcelUtil工具類
場景:在項目中經(jīng)常遇到excel表格導(dǎo)入導(dǎo)出功能,每次都要重復(fù)寫有關(guān)excel 的邏輯,在網(wǎng)上找了一圈沒有找到自己想要的,于是我就自己創(chuàng)建一個工具類,實現(xià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)建了工具類,我們要實現(xiàn)的有導(dǎo)入和導(dǎo)出兩個主要功能
實現(xiàn)導(dǎo)入功能
導(dǎo)入功能實現(xiàn)要考慮幾點:
- Excel表格中的每一列如何和Java實體類中的屬性關(guān)聯(lián)。
- 表頭占幾行是否需要去掉。
- 多個 sheet 頁是否都要導(dǎo)入。
我的代碼如下:
/**
*
* @param file 導(dǎo)入的excel 文件
* @param excelRowToTitleMap excel 的表頭對應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名
* @param excelTitleToPojoMap 列名對應(yīng)java 中實體類屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認全部的 sheet 頁
* @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 的表頭對應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名
* @param excelTitleToPojoMap 列名對應(yīng)java 中實體類屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認全部的 sheet 頁
* @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 頁,就需要過濾其他的
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實體類中的屬性關(guān)聯(lián)關(guān)系。sheetList 列出了需要導(dǎo)入的 sheet 頁,startRow 則是要從第幾行讀取數(shù)據(jù)。這樣上面的幾個問題就都解決了。
實現(xiàn)導(dǎo)出功能
項目中的常見的就是 list 導(dǎo)出到 excel。
同樣的要考慮幾個問題:
- excel 表頭可以自定義中文名。
- 可以指定導(dǎo)出哪些字段。
我的代碼如下:
/**
* 將 list 導(dǎo)入 excel
* @param list list 數(shù)據(jù)列表
* @param obj list 的實體對象
* @param header 表頭數(shù)組 (為 null 時,實體類中屬性名作為表頭)
* @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 頁
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.創(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ù),自動創(chuàng)建下一頁
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 表格導(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兩個參數(shù)可以將excel 列表和實體類中的屬性關(guān)聯(lián)起來(兩個數(shù)組中的元素位置要一一對應(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 的表頭對應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名
* @param excelTitleToPojoMap 列名對應(yīng)java 中實體類屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認全部的 sheet 頁
* @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 的表頭對應(yīng)關(guān)系, key 為第幾列(從 1 開始), value 為 列名
* @param excelTitleToPojoMap 列名對應(yīng)java 中實體類屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認全部的 sheet 頁
* @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 頁,就需要過濾其他的
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 導(dǎo)入 excel
* @param list list 數(shù)據(jù)列表
* @param obj list 的實體對象
* @param header 表頭數(shù)組 (為 null 時,實體類中屬性名作為表頭)
* @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 頁
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.創(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ù),自動創(chuàng)建下一頁
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 表格導(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編寫一個實用的ExcelUtil工具類的詳細內(nèi)容,更多關(guān)于Java ExcelUtil工具類的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
IDEA關(guān)于.properties資源文件的編碼調(diào)整問題
這篇文章主要介紹了IDEA關(guān)于.properties資源文件的編碼調(diào)整問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-06-06
SpringBoot響應(yīng)Json數(shù)據(jù)亂碼通過配置的解決
這篇文章主要介紹了SpringBoot響應(yīng)Json數(shù)據(jù)亂碼通過配置的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

