基于Java編寫(xiě)一個(gè)實(shí)用的ExcelUtil工具類(lèi)
場(chǎng)景:在項(xiàng)目中經(jīng)常遇到excel表格導(dǎo)入導(dǎo)出功能,每次都要重復(fù)寫(xiě)有關(guān)excel 的邏輯,在網(wǎng)上找了一圈沒(méi)有找到自己想要的,于是我就自己創(chuàng)建一個(gè)工具類(lèi),實(shí)現(xiàn)了簡(jiǎn)單的導(dǎo)入和導(dǎo)出功能。
一、首先引入 Maven 依賴(lài)
<!-- 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 工具類(lèi)
創(chuàng)建了工具類(lèi),我們要實(shí)現(xiàn)的有導(dǎo)入和導(dǎo)出兩個(gè)主要功能
實(shí)現(xiàn)導(dǎo)入功能
導(dǎo)入功能實(shí)現(xiàn)要考慮幾點(diǎn):
- Excel表格中的每一列如何和Java實(shí)體類(lèi)中的屬性關(guān)聯(lián)。
- 表頭占幾行是否需要去掉。
- 多個(gè) sheet 頁(yè)是否都要導(dǎo)入。
我的代碼如下:
/**
*
* @param file 導(dǎo)入的excel 文件
* @param excelRowToTitleMap excel 的表頭對(duì)應(yīng)關(guān)系, key 為第幾列(從 1 開(kāi)始), value 為 列名
* @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類(lèi)屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè)
* @param startRow 從第幾行開(kāi)始導(dǎo)入數(shù)據(jù)
* @param clazz list 列表中的元素類(lèi)型
*/
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 開(kāi)始), value 為 列名
* @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類(lèi)屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè)
* @param startRow 從第幾行開(kāi)始導(dǎo)入數(shù)據(jù)
* @param clazz list 列表中的元素類(lèi)型
*/
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è),就需要過(guò)濾其他的
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í)體類(lèi)中的屬性關(guān)聯(lián)關(guān)系。sheetList 列出了需要導(dǎo)入的 sheet 頁(yè),startRow 則是要從第幾行讀取數(shù)據(jù)。這樣上面的幾個(gè)問(wèn)題就都解決了。
實(shí)現(xiàn)導(dǎo)出功能
項(xiàng)目中的常見(jiàn)的就是 list 導(dǎo)出到 excel。
同樣的要考慮幾個(gè)問(wèn)題:
- excel 表頭可以自定義中文名。
- 可以指定導(dǎo)出哪些字段。
我的代碼如下:
/**
* 將 list 導(dǎo)入 excel
* @param list list 數(shù)據(jù)列表
* @param obj list 的實(shí)體對(duì)象
* @param header 表頭數(shù)組 (為 null 時(shí),實(shí)體類(lèi)中屬性名作為表頭)
* @param rows 導(dǎo)出的列 (為 null 導(dǎo)出所有列)
* @param fileName 文件名稱(chēng)
* @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í)體類(lèi)中所有列 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++) {
//超過(guò)最大行數(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();
}
}
我通過(guò)header和rows兩個(gè)參數(shù)可以將excel 列表和實(shí)體類(lèi)中的屬性關(guān)聯(lián)起來(lái)(兩個(gè)數(shù)組中的元素位置要一一對(duì)應(yīng)),然后通過(guò)反射獲取到list列表元素寫(xiě)入到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 開(kāi)始), value 為 列名
* @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類(lèi)屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè)
* @param startRow 從第幾行開(kāi)始導(dǎo)入數(shù)據(jù)
* @param clazz list 列表中的元素類(lèi)型
*/
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 開(kāi)始), value 為 列名
* @param excelTitleToPojoMap 列名對(duì)應(yīng)java 中實(shí)體類(lèi)屬性名
* @param sheetList 要導(dǎo)入的 sheetList 的列表,不傳默認(rèn)全部的 sheet 頁(yè)
* @param startRow 從第幾行開(kāi)始導(dǎo)入數(shù)據(jù)
* @param clazz list 列表中的元素類(lèi)型
*/
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è),就需要過(guò)濾其他的
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í)體類(lèi)中屬性名作為表頭)
* @param rows 導(dǎo)出的列 (為 null 導(dǎo)出所有列)
* @param fileName 文件名稱(chēng)
* @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í)體類(lèi)中所有列 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++) {
//超過(guò)最大行數(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ù)需要添加其他類(lèi)型的轉(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編寫(xiě)一個(gè)實(shí)用的ExcelUtil工具類(lèi)的詳細(xì)內(nèi)容,更多關(guān)于Java ExcelUtil工具類(lèi)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java關(guān)于BeabUtils.copyproperties的用法
這篇文章主要介紹了Java關(guān)于BeabUtils.copyproperties的用法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08
詳解java 中Spring jsonp 跨域請(qǐng)求的實(shí)例
這篇文章主要介紹了詳解java 中Spring jsonp 跨域請(qǐng)求的實(shí)例的相關(guān)資料,jsonp 可用于解決主流瀏覽器的跨域數(shù)據(jù)訪(fǎng)問(wèn)的問(wèn)題,需要的朋友可以參考下2017-08-08
解決mybatis中order by排序無(wú)效問(wèn)題
這篇文章主要介紹了解決mybatis中order by排序無(wú)效問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12
Java搭配Selenium實(shí)現(xiàn)網(wǎng)頁(yè)訪(fǎng)問(wèn)與自動(dòng)截圖的實(shí)戰(zhàn)指南
將Java與Selenium相結(jié)合,不僅可以實(shí)現(xiàn)高效的網(wǎng)頁(yè)自動(dòng)化操作,還能通過(guò)自動(dòng)截圖功能,為測(cè)試和數(shù)據(jù)采集過(guò)程提供直觀的可視化支持,下面我們就來(lái)看看具體實(shí)現(xiàn)方法吧2026-02-02
Java使用Cipher類(lèi)實(shí)現(xiàn)加密的過(guò)程詳解
這篇文章主要介紹了Java使用Cipher類(lèi)實(shí)現(xiàn)加密的過(guò)程詳解,Cipher類(lèi)提供了加密和解密的功能,創(chuàng)建密匙主要使用SecretKeySpec、KeyGenerator和KeyPairGenerator三個(gè)類(lèi)來(lái)創(chuàng)建密匙。感興趣可以了解一下2020-07-07
Mybatis-plus更新字段update_by失敗問(wèn)題
在遇到實(shí)體類(lèi)字段更新不正確的問(wèn)題時(shí),首先復(fù)現(xiàn)問(wèn)題,確定受影響的字段,使用Debug模式查看變量的實(shí)際賦值情況,通過(guò)查看執(zhí)行的SQL語(yǔ)句,確認(rèn)SQL是否正確反映了預(yù)期的更新,如出現(xiàn)問(wèn)題,可以參考Mybatis-plus官網(wǎng)的解決方案2024-09-09
使用JDBC連接Mysql 8.0.11出現(xiàn)了各種錯(cuò)誤的解決
這篇文章主要介紹了使用JDBC連接Mysql 8.0.11出現(xiàn)了各種錯(cuò)誤的解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08

