SpringBoot實(shí)現(xiàn)Excel讀取的實(shí)例教程
這是本人寫的一個(gè)SpringBoot對(duì)Excel讀取的方法,實(shí)測(cè)能用,待提升的地方有很多,有不足之處請(qǐng)多多指點(diǎn)。
Excel2003版(后綴為.xls)最大行數(shù)是65536行,最大列數(shù)是256列。
Excel2007以上的版本(后綴為.xlsx)最大行數(shù)是1048576行,最大列數(shù)是16384列。
提供2種方法讀取:
1.根據(jù)指定的開始和結(jié)束行數(shù)讀取返回結(jié)果,結(jié)果格式為L(zhǎng)ist<Map<String, Object>>
2.根據(jù)指定的開始和結(jié)束行數(shù)讀取返回結(jié)果,結(jié)果格式為L(zhǎng)ist<POJO(傳入的實(shí)體類)>
請(qǐng)根據(jù)實(shí)際內(nèi)存堆可用大小進(jìn)行讀取,太多可進(jìn)行分段讀取(類似分頁的原理)
讀取Excel所需要的幾個(gè)類</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實(shí)體類
package com.cly.utils.Excel;/** * @author : CLy * @ClassName : ExcelPOJO * @date : 2020/7/9 17:13 * 實(shí)體類所有成員變量都需要有GET,SET方法 * 所有成員變量都要加上注解@excelRescoure(value = '?'),?為Excel真實(shí)列名,必須一一對(duì)應(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自定義注解(用于實(shí)體類讀取)
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ù)文件后綴名類型獲取對(duì)應(yīng)的工作簿對(duì)象 * @param inputStream 讀取文件的輸入流 * @param fileType 文件后綴名類型(xls或xlsx) * @return 包含文件數(shù)據(jù)的工作簿對(duì)象 */ 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é)計(jì)數(shù)法,取一位整數(shù),如取小數(shù),值如0.0,取小數(shù)點(diǎn)后幾位就寫幾個(gè)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)為L(zhǎng)ist<Map<String,Object>>輸出 * workbook:已連接的工作薄 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0,0開始,傳過來是EXcel的行數(shù)值默認(rèn)從1開始,這里已處理減1) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * ExistTop:是否存在頭部(如存在則讀取數(shù)據(jù)時(shí)會(huì)把頭部拼接到對(duì)應(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<>(); //聲明一個(gè)Excel頭部函數(shù) ArrayList<String> top = new ArrayList<>(); //解析sheet(sheet是Excel腳頁) /** *此處會(huì)讀取所有腳頁的行數(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); // 校驗(yàn)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ù)時(shí)會(huì)把頭部拼接到對(duì)應(yīng)數(shù)據(jù),若無則為當(dāng)前列數(shù)) * 返回一個(gè)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é)束行值大,請(qǐng)重新輸入正確的行數(shù)'); List<Map<String, Object>> error = null; return error; } //聲明返回的結(jié)果集 List<Map<String, Object>> result = new ArrayList<>(); //聲明一個(gè)工作薄 Workbook workbook = null; //聲明一個(gè)文件輸入流 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 + ' 錯(cuò)誤信息:' + e.getMessage()); } finally { try { if (null != workbook) { workbook.close(); } if (null != inputStream) { inputStream.close(); } } catch (Exception e) { logger.warn('關(guān)閉數(shù)據(jù)流出錯(cuò)!錯(cuò)誤信息:' + e.getMessage()); return null; } } return result; }/**==============================================================================================================================**/ /** * 方法二 * 根據(jù)給定的實(shí)體類中賦值的注解值讀取Excel * fileName:Excel文件路徑 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * Class<T>:傳過來的實(shí)體類類型 * 返回一個(gè)List<T>:T為實(shí)體類 */ 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é)束行值大,請(qǐng)重新輸入正確的行數(shù)'); List<Object> error = null; return error; } //聲明返回的結(jié)果集 List<Object> result = new ArrayList<>(); //聲明一個(gè)工作薄 Workbook workbook = null; //聲明一個(gè)文件輸入流 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 + ' 錯(cuò)誤信息:' + e.getMessage()); } finally { try { if (null != workbook) { workbook.close(); } if (null != inputStream) { inputStream.close(); } } catch (Exception e) { logger.warn('關(guān)閉數(shù)據(jù)流出錯(cuò)!錯(cuò)誤信息:' + e.getMessage()); return null; } } return result; } /** * 處理Excel內(nèi)容轉(zhuǎn)為L(zhǎng)ist<T>輸出 * workbook:已連接的工作薄 * StatrRow:讀取的開始行數(shù)(默認(rèn)填0,0開始,傳過來是EXcel的行數(shù)值默認(rèn)從1開始,這里已處理減1) * EndRow:讀取的結(jié)束行數(shù)(填-1為全部) * Class<T>:所映射的實(shí)體類 */ 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); // 校驗(yàn)sheet是否合法 if (sheet == null) { continue; } //獲取頭部數(shù)據(jù) //聲明頭部數(shù)據(jù)數(shù)列對(duì)象 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))); } //獲取實(shí)體類的成原變量 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對(duì)應(yīng)列的數(shù)據(jù) String celldata = convertCellValueToString(row.getCell(Integer.parseInt(map.getKey()))); //使用發(fā)射 //獲取實(shí)體類T中指定成員變量的對(duì)象 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; } /** * 獲取對(duì)應(yīng)的實(shí)體類成員 * */ 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('實(shí)體類:' + T + '不存在成員變量'); return null; } return result; }}5.測(cè)試類
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ù)時(shí)會(huì)把頭部拼接到對(duì)應(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>:傳過來的實(shí)體類類型 */ List<Object> result2 = excelRead.ReadExcelByPOJO('D:.xls',2,10,ExcelPOJO.class); System.out.println(result2.size()); System.out.println(result2); }} 6.運(yùn)行結(jié)果和說明
exce表格數(shù)據(jù)
方法一的運(yùn)行結(jié)果
1.ture:key為列名
2.false:key為第幾列列數(shù)
方法二的運(yùn)行結(jié)果
實(shí)體類的所有成員變量一定要加上自定義注釋@excelRescoure,不然會(huì)報(bào)錯(cuò)
還有很多不足的地方,請(qǐng)多多指點(diǎn),希望能給你帶來幫助。
SpringBoot實(shí)現(xiàn)內(nèi)存數(shù)據(jù)導(dǎo)出成Excel在另一篇文章 文章地址:https://www.jb51.net/article/202767.htm
總結(jié)到此這篇關(guān)于SpringBoot實(shí)現(xiàn)Excel讀取的文章就介紹到這了,更多相關(guān)SpringBoot實(shí)現(xiàn)Excel讀取內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
