SpringBoot實現(xiàn)Excel讀取的實例教程
前言
這是本人寫的一個SpringBoot對Excel讀取的方法,實測能用,待提升的地方有很多,有不足之處請多多指點。
Excel2003版(后綴為.xls)最大行數(shù)是65536行,最大列數(shù)是256列。
Excel2007以上的版本(后綴為.xlsx)最大行數(shù)是1048576行,最大列數(shù)是16384列。
提供2種方法讀取:
1.根據(jù)指定的開始和結(jié)束行數(shù)讀取返回結(jié)果,結(jié)果格式為List<Map<String, Object>>
2.根據(jù)指定的開始和結(jié)束行數(shù)讀取返回結(jié)果,結(jié)果格式為List<POJO(傳入的實體類)>
請根據(jù)實際內(nèi)存堆可用大小進(jìn)行讀取,太多可進(jìn)行分段讀?。愃品猪摰脑恚?/p>
讀取Excel所需要的幾個類
1.在pom.xml加上依賴
</dependencies> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.0.1</version> </dependency> </dependencies>
2.ExcelPOJO實體類
package com.cly.utils.Excel; /** * @author : CLy * @ClassName : ExcelPOJO * @date : 2020/7/9 17:13 * 實體類所有成員變量都需要有GET,SET方法 * 所有成員變量都要加上注解@excelRescoure(value = "?"),?為Excel真實列名,必須一一對應(yīng) * @excelRescoure(value = "?"),?可為空,需要用到才賦值 * 成員變量目前只允許String,Double,Interge,Float **/ public class ExcelPOJO { public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPasswork() { return passwork; } public void setPasswork(String passwork) { this.passwork = passwork; } public String getLook() { return look; } public void setLook(String look) { this.look = look; } @excelRescoure(value = "XM") private String name; @excelRescoure(value = "SFZH") private String passwork; @excelRescoure() private String look; @Override public String toString(){ return "name:"+this.getName()+",passwork:"+this.getPasswork()+",look:"+this.getLook(); } public ExcelPOJO() {} }
3.@interface自定義注解(用于實體類讀取)
package com.cly.utils.Excel; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @author : CLy * @ClassName : myRescoure * @date : 2020/7/10 9:31 **/ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface excelRescoure { String value() default "";//默認(rèn)為空 }
4.excelRead類(讀取Excel數(shù)據(jù)類)有很多冗余的代碼,可抽離出來
package com.cly.utils.Excel; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.sun.org.apache.bcel.internal.generic.NEW; import org.apache.poi.hssf.usermodel.HSSFWorkbook; import org.apache.poi.ss.formula.functions.T; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.xml.transform.Source; import java.beans.IntrospectionException; import java.beans.PropertyDescriptor; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.*; import java.text.DecimalFormat; import java.util.*; /** * @author : CLy * @ClassName : excelRead * @date : 2020/7/9 11:08 **/ public class excelRead { //日志輸出 private static Logger logger = LoggerFactory.getLogger(excelRead.class); //定義excel類型 private static final String XLS = "xls"; private static final String XLSX = "xlsx"; /** * 根據(jù)文件后綴名類型獲取對應(yīng)的工作簿對象 * @param inputStream 讀取文件的輸入流 * @param fileType 文件后綴名類型(xls或xlsx) * @return 包含文件數(shù)據(jù)的工作簿對象 */ private static Workbook getWorkbook(InputStream inputStream, String fileType) throws IOException { //用自帶的方法新建工作薄 Workbook workbook = WorkbookFactory.create(inputStream); //后綴判斷有版本轉(zhuǎn)換問題 //Workbook workbook = null; //if (fileType.equalsIgnoreCase(XLS)) { // workbook = new HSSFWorkbook(inputStream); //} else if (fileType.equalsIgnoreCase(XLSX)) { // workbook = new XSSFWorkbook(inputStream); //} return workbook; } /** * 將單元格內(nèi)容轉(zhuǎn)換為字符串 * @param cell * @return */ private static String convertCellValueToString(Cell cell) { if (cell == null) { return null; } String returnValue = null; switch (cell.getCellType()) { case NUMERIC: //數(shù)字 Double doubleValue = cell.getNumericCellValue(); // 格式化科學(xué)計數(shù)法,取一位整數(shù),如取小數(shù),值如0.0,取小數(shù)點后幾位就寫幾個0 DecimalFormat df = new DecimalFormat("0"); returnValue = df.format(doubleValue); break; case STRING: //字符串 returnValue = cell.getStringCellValue(); break; case BOOLEAN: //布爾 Boolean booleanValue = cell.getBooleanCellValue(); returnValue = booleanValue.toString(); break; case BLANK: // 空值 break; case FORMULA: // 公式 returnValue = cell.getCellFormula(); break; case ERROR: // 故障 break; default: break; } return returnValue; } /** * 處理Excel內(nèi)容轉(zhuǎn)為List<Map<String,Object>>輸出 * workbook:已連接的工作薄 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0,0開始,傳過來是EXcel的行數(shù)值默認(rèn)從1開始,這里已處理減1) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * ExistTop:是否存在頭部(如存在則讀取數(shù)據(jù)時會把頭部拼接到對應(yīng)數(shù)據(jù),若無則為當(dāng)前列數(shù)) */ private static List<Map<String, Object>> HandleData(Workbook workbook, int StatrRow, int EndRow, boolean ExistTop) { //聲明返回結(jié)果集result List<Map<String, Object>> result = new ArrayList<>(); //聲明一個Excel頭部函數(shù) ArrayList<String> top = new ArrayList<>(); //解析sheet(sheet是Excel腳頁) /** *此處會讀取所有腳頁的行數(shù)據(jù),若只想讀取指定頁,不要for循環(huán),直接給sheetNum賦值,腳頁從0開始(通常情況Excel都只有一頁,所以此處未進(jìn)行進(jìn)一步處理) */ for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) { Sheet sheet = workbook.getSheetAt(sheetNum); // 校驗sheet是否合法 if (sheet == null) { continue; } //如存在頭部,處理頭部數(shù)據(jù) if (ExistTop) { int firstRowNum = sheet.getFirstRowNum(); Row firstRow = sheet.getRow(firstRowNum); if (null == firstRow) { logger.warn("解析Excel失敗,在第一行沒有讀取到任何數(shù)據(jù)!"); } for (int i = 0; i < firstRow.getLastCellNum(); i++) { top.add(convertCellValueToString(firstRow.getCell(i))); } } //處理Excel數(shù)據(jù)內(nèi)容 int endRowNum; //獲取結(jié)束行數(shù) if (EndRow == -1) { endRowNum = sheet.getPhysicalNumberOfRows(); } else { endRowNum = EndRow <= sheet.getPhysicalNumberOfRows() ? EndRow : sheet.getPhysicalNumberOfRows(); } //遍歷行數(shù) for (int i = StatrRow - 1; i < endRowNum; i++) { Row row = sheet.getRow(i); if (null == row) { continue; } Map<String, Object> map = new HashMap<>(); //獲取所有列數(shù)據(jù) for (int y = 0; y < row.getLastCellNum(); y++) { if (top.size() > 0) { if (top.size() >= y) { map.put(top.get(y), convertCellValueToString(row.getCell(y))); } else { map.put(String.valueOf(y + 1), convertCellValueToString(row.getCell(y))); } } else { map.put(String.valueOf(y + 1), convertCellValueToString(row.getCell(y))); } } result.add(map); } } return result; } /** * 方法一 * 根據(jù)行數(shù)和列數(shù)讀取Excel * fileName:Excel文件路徑 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * ExistTop:是否存在頭部(如存在則讀取數(shù)據(jù)時會把頭部拼接到對應(yīng)數(shù)據(jù),若無則為當(dāng)前列數(shù)) * 返回一個List<Map<String,Object>> */ public static List<Map<String, Object>> ReadExcelByRC(String fileName, int StatrRow, int EndRow, boolean ExistTop) { //判斷輸入的開始值是否少于等于結(jié)束值 if (StatrRow > EndRow && EndRow != -1) { logger.warn("輸入的開始行值比結(jié)束行值大,請重新輸入正確的行數(shù)"); List<Map<String, Object>> error = null; return error; } //聲明返回的結(jié)果集 List<Map<String, Object>> result = new ArrayList<>(); //聲明一個工作薄 Workbook workbook = null; //聲明一個文件輸入流 FileInputStream inputStream = null; try { // 獲取Excel后綴名,判斷文件類型 String fileType = fileName.substring(fileName.lastIndexOf(".") + 1); // 獲取Excel文件 File excelFile = new File(fileName); if (!excelFile.exists()) { logger.warn("指定的Excel文件不存在!"); return null; } // 獲取Excel工作簿 inputStream = new FileInputStream(excelFile); workbook = getWorkbook(inputStream, fileType); //處理Excel內(nèi)容 result = HandleData(workbook, StatrRow, EndRow, ExistTop); } catch (Exception e) { logger.warn("解析Excel失敗,文件名:" + fileName + " 錯誤信息:" + e.getMessage()); } finally { try { if (null != workbook) { workbook.close(); } if (null != inputStream) { inputStream.close(); } } catch (Exception e) { logger.warn("關(guān)閉數(shù)據(jù)流出錯!錯誤信息:" + e.getMessage()); return null; } } return result; } /**==============================================================================================================================**/ /** * 方法二 * 根據(jù)給定的實體類中賦值的注解值讀取Excel * fileName:Excel文件路徑 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * Class<T>:傳過來的實體類類型 * 返回一個List<T>:T為實體類 */ public static List<Object> ReadExcelByPOJO(String fileName, int StatrRow, int EndRow, Class t) throws InvocationTargetException, IntrospectionException, InstantiationException, IllegalAccessException, NoSuchFieldException { //判斷輸入的開始值是否少于等于結(jié)束值 if (StatrRow > EndRow && EndRow != -1) { logger.warn("輸入的開始行值比結(jié)束行值大,請重新輸入正確的行數(shù)"); List<Object> error = null; return error; } //聲明返回的結(jié)果集 List<Object> result = new ArrayList<>(); //聲明一個工作薄 Workbook workbook = null; //聲明一個文件輸入流 FileInputStream inputStream = null; try { // 獲取Excel后綴名,判斷文件類型 String fileType = fileName.substring(fileName.lastIndexOf(".") + 1); // 獲取Excel文件 File excelFile = new File(fileName); if (!excelFile.exists()) { logger.warn("指定的Excel文件不存在!"); return null; } // 獲取Excel工作簿 inputStream = new FileInputStream(excelFile); workbook = getWorkbook(inputStream, fileType); //處理Excel內(nèi)容 result = HandleDataPOJO(workbook, StatrRow, EndRow, t); } catch (Exception e) { logger.warn("解析Excel失敗,文件名:" + fileName + " 錯誤信息:" + e.getMessage()); } finally { try { if (null != workbook) { workbook.close(); } if (null != inputStream) { inputStream.close(); } } catch (Exception e) { logger.warn("關(guān)閉數(shù)據(jù)流出錯!錯誤信息:" + e.getMessage()); return null; } } return result; } /** * 處理Excel內(nèi)容轉(zhuǎn)為List<T>輸出 * workbook:已連接的工作薄 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0,0開始,傳過來是EXcel的行數(shù)值默認(rèn)從1開始,這里已處理減1) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * Class<T>:所映射的實體類 */ private static <t> List<Object> HandleDataPOJO(Workbook workbook, int StatrRow, int EndRow, Class<?> t) throws IntrospectionException, NoSuchFieldException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException { //聲明返回的結(jié)果集 List<Object> result = new ArrayList<Object>(); //解析sheet(sheet是Excel腳頁) for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) { Sheet sheet = workbook.getSheetAt(sheetNum); // 校驗sheet是否合法 if (sheet == null) { continue; } //獲取頭部數(shù)據(jù) //聲明頭部數(shù)據(jù)數(shù)列對象 ArrayList<String> top = new ArrayList<>(); //獲取Excel第一行數(shù)據(jù) int firstRowNum = sheet.getFirstRowNum(); Row firstRow = sheet.getRow(firstRowNum); if (null == firstRow) { logger.warn("解析Excel失敗,在第一行沒有讀取到任何數(shù)據(jù)!"); return null; } for (int i = 0; i < firstRow.getLastCellNum(); i++) { top.add(convertCellValueToString(firstRow.getCell(i))); } //獲取實體類的成原變量 Map<String, Object> POJOfields = getPOJOFieldAndValue(t); //判斷所需要的數(shù)據(jù)列 Map<String, Object> exceltoPOJO = new HashMap<>(); for (int i = 0; i < top.size(); i++) { if (POJOfields.get(top.get(i)) != null && !"".equals(POJOfields.get(top.get(i)))) { exceltoPOJO.put(String.valueOf(i), POJOfields.get(top.get(i))); } } /*處理Excel數(shù)據(jù)內(nèi)容*/ int endRowNum; //獲取結(jié)束行數(shù) if (EndRow == -1) { endRowNum = sheet.getPhysicalNumberOfRows(); } else { endRowNum = EndRow <= sheet.getPhysicalNumberOfRows() ? EndRow : sheet.getPhysicalNumberOfRows(); } List<Map<String, Object>> mapList = new ArrayList<>(); //遍歷行數(shù) for (int i = StatrRow - 1; i < endRowNum; i++) { Row row = sheet.getRow(i); if (null == row) { continue; } //獲取需要的列數(shù)據(jù) t texcel = (t) t.newInstance(); for (Map.Entry<String, Object> map : exceltoPOJO.entrySet()) { //獲取Exceld對應(yīng)列的數(shù)據(jù) String celldata = convertCellValueToString(row.getCell(Integer.parseInt(map.getKey()))); //使用發(fā)射 //獲取實體類T中指定成員變量的對象 PropertyDescriptor pd = new PropertyDescriptor((String) map.getValue(), texcel.getClass()); //獲取成員變量的set方法 Method method = pd.getWriteMethod(); //判斷成員變量的類型 Field field = texcel.getClass().getDeclaredField((String) map.getValue()); String object = field.getGenericType().getTypeName(); if (object.endsWith("String")) { //執(zhí)行set方法 method.invoke(texcel, celldata); } if (object.endsWith("Double")) { Double middata = Double.valueOf(celldata); //執(zhí)行set方法 method.invoke(texcel, middata); } if (object.endsWith("Float")) { Float middata = Float.valueOf(celldata); //執(zhí)行set方法 method.invoke(texcel, middata); } if (object.endsWith("Integer")) { Integer middata = Integer.parseInt(celldata); //執(zhí)行set方法 method.invoke(texcel, middata); } } result.add(texcel); } } return result; } /** * 獲取對應(yīng)的實體類成員 * */ private static Map<String, Object> getPOJOFieldAndValue(Class T) { //聲明返回結(jié)果集 Map<String, Object> result = new HashMap<>(); Field[] fields = T.getDeclaredFields();//獲取屬性名 if (fields != null) { for (Field field : fields) { excelRescoure Rescoure = field.getAnnotation(excelRescoure.class); if (Rescoure.value() != null && !"".equals(Rescoure.value())) { result.put(Rescoure.value(), field.getName()); } } } else { logger.warn("實體類:" + T + "不存在成員變量"); return null; } return result; } }
5.測試類
package com.cly.utils.Excel; import java.util.*; /** * @author : CLy * @ClassName : Readtest * @date : 2020/7/9 16:31 **/ public class Readtest { public static void main(String[] args) throws Exception { /** 方法一 * fileName:Excel文件路徑 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * ExistTop:是否存在頭部(如存在則讀取數(shù)據(jù)時會把頭部拼接到對應(yīng)數(shù)據(jù)作為KEY,若無則KEY為當(dāng)前列數(shù)) */ List<Map<String,Object>> result =excelRead.ReadExcelByRC("D:.xls",2,10,false); System.out.println(result.size()); System.out.println(result); /** * 方法二 * ReadExcelByPOJO(String fileName, int StatrRow, int EndRow, Class t) * fileName:Excel文件路徑 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * Class<T>:傳過來的實體類類型 */ List<Object> result2 = excelRead.ReadExcelByPOJO("D:.xls",2,10,ExcelPOJO.class); System.out.println(result2.size()); System.out.println(result2); } }
6.運行結(jié)果和說明
exce表格數(shù)據(jù)
方法一的運行結(jié)果
1.ture:key為列名
2.false:key為第幾列列數(shù)
方法二的運行結(jié)果
實體類的所有成員變量一定要加上自定義注釋@excelRescoure,不然會報錯
還有很多不足的地方,請多多指點,希望能給你帶來幫助。
SpringBoot實現(xiàn)內(nèi)存數(shù)據(jù)導(dǎo)出成Excel在另一篇文章 文章地址:http://www.dbjr.com.cn/article/202767.htm
總結(jié)
到此這篇關(guān)于SpringBoot實現(xiàn)Excel讀取的文章就介紹到這了,更多相關(guān)SpringBoot實現(xiàn)Excel讀取內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot中EasyExcel實現(xiàn)Excel文件的導(dǎo)入導(dǎo)出
- SpringBoot+easypoi實現(xiàn)數(shù)據(jù)的Excel導(dǎo)出
- SpringBoot整合EasyExcel實現(xiàn)文件導(dǎo)入導(dǎo)出
- SpringBoot集成EasyExcel實現(xiàn)Excel導(dǎo)入的方法
- SpringBoot內(nèi)存數(shù)據(jù)導(dǎo)出成Excel的實現(xiàn)方法
- 使用Springboot+poi上傳并處理百萬級數(shù)據(jù)EXCEL
- 在SpringBoot: SpringBoot里面創(chuàng)建導(dǎo)出Excel的接口教程
- SpringBoot讀取excel表格的示例代碼
- 解決springboot 多線程使用MultipartFile讀取excel文件內(nèi)容報錯問題
- SpringBoot中使用JeecgBoot的Autopoi導(dǎo)出Excel的方法步驟
- SpringBoot整合POI導(dǎo)出通用Excel的方法示例
- Springboot POI導(dǎo)出Excel(瀏覽器)
- 基于SpringBoot框架管理Excel和PDF文件類型
- SpringBoot實現(xiàn)Excel文件批量上傳導(dǎo)入數(shù)據(jù)庫
- Springboot實現(xiàn)導(dǎo)入導(dǎo)出Excel的方法
相關(guān)文章
Spring Boot 2.x基礎(chǔ)教程之使用@Scheduled實現(xiàn)定時任務(wù)的方法
在Spring Boot中編寫定時任務(wù)是非常簡單的事,下面通過實例介紹如何在Spring Boot中創(chuàng)建定時任務(wù),實現(xiàn)每過5秒輸出一個當(dāng)前時間,感興趣的朋友跟隨小編一起看看吧2021-07-07一文帶你學(xué)會Java網(wǎng)絡(luò)編程
網(wǎng)絡(luò)編程是指編寫運行在多個設(shè)備(計算機)的程序,這些設(shè)備都通過網(wǎng)絡(luò)連接起來。這篇文章將帶大家深入了解一下Java的網(wǎng)絡(luò)編程,需要的可以了解一下2022-08-08Java多線程實現(xiàn)TCP網(wǎng)絡(luò)Socket編程(C/S通信)
這篇文章主要介紹了Java多線程實現(xiàn)TCP網(wǎng)絡(luò)Socket編程(C/S通信),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Java并發(fā)編程之ConcurrentLinkedQueue源碼詳解
今天帶小伙伴們學(xué)習(xí)一下Java并發(fā)編程之Java ConcurrentLinkedQueue源碼,本篇文章詳細(xì)分析了ConcurrentLinkedQueue源碼,有代碼示例,對正在學(xué)習(xí)java的小伙伴們很有幫助喲,需要的朋友可以參考下2021-05-05application作用域?qū)崿F(xiàn)用戶登錄擠掉之前登錄用戶代碼
這篇文章主要介紹了application作用域?qū)崿F(xiàn)用戶登錄擠掉之前登錄用戶代碼,具有一定參考價值,需要的朋友可以了解下。2017-11-11