SpringBoot实现Excel读取

SpringBoot实现Excel读取

SpringBoot实现内存数据导出成Excel在另一篇文章 文章地址:
https://blog.csdn.net/qq_45367825/article/details/111415276

这是本人写的一个SpringBoot对Excel读取的方法,实测能用,待提升的地方有很多,有不足之处请多多指点。
Excel2003版(后缀为.xls)最大行数是65536行,最大列数是256列。
Excel2007以上的版本(后缀为.xlsx)最大行数是1048576行,最大列数是16384列。
提供2种方法读取:
1.根据指定的开始和结束行数读取返回结果,结果格式为List<Map<String, Object>>
2.根据指定的开始和结束行数读取返回结果,结果格式为List<POJO(传入的实体类)>

请根据实际内存堆可用大小进行读取,太多可进行分段读取(类似分页的原理)


读取Excel所需要的几个类

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真实列名,必须一一对应
 * @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 "";//默认为空
}

4.excelRead类(读取Excel数据类)有很多冗余的代码,可抽离出来

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";

    /**
     * 根据文件后缀名类型获取对应的工作簿对象
     * @param inputStream 读取文件的输入流
     * @param fileType    文件后缀名类型(xls或xlsx)
     * @return 包含文件数据的工作簿对象
     */
    private static Workbook getWorkbook(InputStream inputStream, String fileType) throws IOException {
        //用自带的方法新建工作薄
        Workbook workbook = WorkbookFactory.create(inputStream);
        //后缀判断有版本转换问题
        //Workbook workbook = null;
        //if (fileType.equalsIgnoreCase(XLS)) {
        //    workbook = new HSSFWorkbook(inputStream);
        //} else if (fileType.equalsIgnoreCase(XLSX)) {
        //    workbook = new XSSFWorkbook(inputStream);
        //}
        return workbook;
    }


    /**
     * 将单元格内容转换为字符串
     * @param cell
     * @return
     */
    private static String convertCellValueToString(Cell cell) {
        if (cell == null) {
            return null;
        }
        String returnValue = null;
        switch (cell.getCellType()) {
            case NUMERIC:   //数字
                Double doubleValue = cell.getNumericCellValue();
                // 格式化科学计数法,取一位整数,如取小数,值如0.0,取小数点后几位就写几个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内容转为List<Map<String,Object>>输出
     * workbook:已连接的工作薄
     * StatrRow:读取的开始行数(默认填0,0开始,传过来是EXcel的行数值默认从1开始,这里已处理减1)
     * EndRow:读取的结束行数(填-1为全部)
     * ExistTop:是否存在头部(如存在则读取数据时会把头部拼接到对应数据,若无则为当前列数)
     */
    private static List<Map<String, Object>> HandleData(Workbook workbook, int StatrRow, int EndRow, boolean ExistTop) {
        //声明返回结果集result
        List<Map<String, Object>> result = new ArrayList<>();
        //声明一个Excel头部函数
        ArrayList<String> top = new ArrayList<>();
        //解析sheet(sheet是Excel脚页)
        /**
        *此处会读取所有脚页的行数据,若只想读取指定页,不要for循环,直接给sheetNum赋值,脚页从0开始(通常情况Excel都只有一页,所以此处未进行进一步处理)
        */
        for (int sheetNum = 0; sheetNum < workbook.getNumberOfSheets(); sheetNum++) {
            Sheet sheet = workbook.getSheetAt(sheetNum);
            // 校验sheet是否合法
            if (sheet == null) {
                continue;
            }
            //如存在头部,处理头部数据
            if (ExistTop) {
                int firstRowNum = sheet.getFirstRowNum();
                Row firstRow = sheet.getRow(firstRowNum);
                if (null == firstRow) {
                    logger.warn("解析Excel失败,在第一行没有读取到任何数据!");
                }
                for (int i = 0; i < firstRow.getLastCellNum(); i++) {
                    top.add(convertCellValueToString(firstRow.getCell(i)));
                }
            }
            //处理Excel数据内容
            int endRowNum;
            //获取结束行数
            if (EndRow == -1) {
                endRowNum = sheet.getPhysicalNumberOfRows();
            } else {
                endRowNum = EndRow <= sheet.getPhysicalNumberOfRows() ? EndRow : sheet.getPhysicalNumberOfRows();
            }
            //遍历行数
            for (int i = StatrRow - 1; i < endRowNum; i++) {
                Row row = sheet.getRow(i);
                if (null == row) {
                    continue;
                }
                Map<String, Object> map = new HashMap<>();
                //获取所有列数据
                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;
    }

    /**
     * 方法一
     * 根据行数和列数读取Excel
     * fileName:Excel文件路径
     * StatrRow:读取的开始行数(默认填0)
     * EndRow:读取的结束行数(填-1为全部)
     * ExistTop:是否存在头部(如存在则读取数据时会把头部拼接到对应数据,若无则为当前列数)
     * 返回一个List<Map<String,Object>>
     */
    public static List<Map<String, Object>> ReadExcelByRC(String fileName, int StatrRow, int EndRow, boolean ExistTop) {
        //判断输入的开始值是否少于等于结束值
        if (StatrRow > EndRow && EndRow != -1) {
            logger.warn("输入的开始行值比结束行值大,请重新输入正确的行数");
            List<Map<String, Object>> error = null;
            return error;
        }
        //声明返回的结果集
        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内容
            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("关闭数据流出错!错误信息:" + e.getMessage());
                return null;
            }
        }
        return result;
    }


/**==============================================================================================================================**/

    /**
     * 方法二
     * 根据给定的实体类中赋值的注解值读取Excel
     * fileName:Excel文件路径
     * StatrRow:读取的开始行数(默认填0)
     * EndRow:读取的结束行数(填-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 {
        //判断输入的开始值是否少于等于结束值
        if (StatrRow > EndRow && EndRow != -1) {
            logger.warn("输入的开始行值比结束行值大,请重新输入正确的行数");
            List<Object> error = null;
            return error;
        }
        //声明返回的结果集
        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内容
            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("关闭数据流出错!错误信息:" + e.getMessage());
                return null;
            }
        }
        return result;
    }


    /**
     * 处理Excel内容转为List<T>输出
     * workbook:已连接的工作薄
     * StatrRow:读取的开始行数(默认填0,0开始,传过来是EXcel的行数值默认从1开始,这里已处理减1)
     * EndRow:读取的结束行数(填-1为全部)
     * Class<T>:所映射的实体类
     */
    private static <t> List<Object> HandleDataPOJO(Workbook workbook, int StatrRow, int EndRow, Class<?> t) throws IntrospectionException, NoSuchFieldException, IllegalAccessException, InstantiationException, InvocationTargetException, ClassNotFoundException {
        //声明返回的结果集
        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;
            }
            //获取头部数据
            //声明头部数据数列对象
            ArrayList<String> top = new ArrayList<>();
            //获取Excel第一行数据
            int firstRowNum = sheet.getFirstRowNum();
            Row firstRow = sheet.getRow(firstRowNum);
            if (null == firstRow) {
                logger.warn("解析Excel失败,在第一行没有读取到任何数据!");
                return null;
            }
            for (int i = 0; i < firstRow.getLastCellNum(); i++) {
                top.add(convertCellValueToString(firstRow.getCell(i)));
            }
            //获取实体类的成原变量
            Map<String, Object> POJOfields = getPOJOFieldAndValue(t);
            //判断所需要的数据列
            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数据内容*/
            int endRowNum;
            //获取结束行数
            if (EndRow == -1) {
                endRowNum = sheet.getPhysicalNumberOfRows();
            } else {
                endRowNum = EndRow <= sheet.getPhysicalNumberOfRows() ? EndRow : sheet.getPhysicalNumberOfRows();
            }
            List<Map<String, Object>> mapList = new ArrayList<>();
            //遍历行数
            for (int i = StatrRow - 1; i < endRowNum; i++) {
                Row row = sheet.getRow(i);
                if (null == row) {
                    continue;
                }
                //获取需要的列数据
                t texcel = (t) t.newInstance();
                for (Map.Entry<String, Object> map : exceltoPOJO.entrySet()) {
                    //获取Exceld对应列的数据
                    String celldata = convertCellValueToString(row.getCell(Integer.parseInt(map.getKey())));
                    //使用发射
                    //获取实体类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")) {
                        //执行set方法
                        method.invoke(texcel, celldata);
                    }
                    if (object.endsWith("Double")) {
                        Double middata = Double.valueOf(celldata);
                        //执行set方法
                        method.invoke(texcel, middata);
                    }
                    if (object.endsWith("Float")) {
                        Float middata = Float.valueOf(celldata);
                        //执行set方法
                        method.invoke(texcel, middata);
                    }
                    if (object.endsWith("Integer")) {
                        Integer middata = Integer.parseInt(celldata);
                        //执行set方法
                        method.invoke(texcel, middata);
                    }
                }
                result.add(texcel);
            }
        }
        return result;
    }

    /**
     * 获取对应的实体类成员
     * */
    private static Map<String, Object> getPOJOFieldAndValue(Class T) {
        //声明返回结果集
        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:读取的开始行数(默认填0)
       * EndRow:读取的结束行数(填-1为全部)
       * ExistTop:是否存在头部(如存在则读取数据时会把头部拼接到对应数据作为KEY,若无则KEY为当前列数)
       */
        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:读取的开始行数(默认填0)
        * EndRow:读取的结束行数(填-1为全部)
        * Class<T>:传过来的实体类类型
        */
        List<Object> result2 = excelRead.ReadExcelByPOJO("D:.xls",2,10,ExcelPOJO.class);
        System.out.println(result2.size());
        System.out.println(result2);
        }
}
        

6.运行结果和说明

exce表格数据
在这里插入图片描述

  • 方法一的运行结果

1.ture:key为列名

在这里插入图片描述

2.false:key为第几列列数

在这里插入图片描述

  • 方法二的运行结果

实体类的所有成员变量一定要加上自定义注释@excelRescoure,不然会报错

在这里插入图片描述
在这里插入图片描述


美丽的分割域


新代码区

  • 修改内容

1、在实体类方法设置值时加上了空值判断(修改了实体类空值报错bug)
2、重新处理了Excel多页选择的问题,旧版代码是扫描全部表,对于多表页的Excel未能进行表的选择

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.*;
import org.apache.poi.util.StringUtil;
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";

    /**
     * 根据文件后缀名类型获取对应的工作簿对象
     * @param inputStream 读取文件的输入流
     * @param fileType    文件后缀名类型(xls或xlsx)
     * @return 包含文件数据的工作簿对象
     */
    private static Workbook getWorkbook(InputStream inputStream, String fileType) throws IOException {
        Workbook workbook = WorkbookFactory.create(inputStream);
        //Workbook workbook = null;
        //if (fileType.equalsIgnoreCase(XLS)) {
        //    workbook = new HSSFWorkbook(inputStream);
        //} else if (fileType.equalsIgnoreCase(XLSX)) {
        //    workbook = new XSSFWorkbook(inputStream);
        //}
        return workbook;
    }


    /**
     * 将单元格内容转换为字符串
     * @param cell
     * @return
     */
    private static String convertCellValueToString(Cell cell) {
        if (cell == null) {
            return null;
        }
        String returnValue = null;
        switch (cell.getCellType()) {
            case NUMERIC:   //数字
                Double doubleValue = cell.getNumericCellValue();
                // 格式化科学计数法,取一位整数,如取小数,值如0.0,取小数点后几位就写几个0
                DecimalFormat df = new DecimalFormat("0.00");
                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内容转为List<Map<String,Object>>输出
     * workbook:已连接的工作薄
     * StatrRow:读取的开始行数(默认填0,0开始,传过来是EXcel的行数值默认从1开始,这里已处理减1)
     * EndRow:读取的结束行数(填-1为全部)
     * ExistTop:是否存在头部(如存在则读取数据时会把头部拼接到对应数据,若无则为当前列数)
     */
    private static List<List<Map<String, Object>>> HandleData(Workbook workbook, int StatrRow, int EndRow, boolean ExistTop,String SheetNum) {
        //声明返回结果集result
        List<List<Map<String, Object>>> result = new ArrayList<>();
        //解析sheet(sheet是Excel脚页)
        if (SheetNum =="-1"){
            Integer[] SheetNumArray = new  Integer[workbook.getNumberOfSheets()];
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                SheetNumArray[i] = i;
            }
            result = ReadData(workbook, StatrRow, EndRow, ExistTop, SheetNumArray);
        }else {
            String[] split = SheetNum.split(",");
            Integer[] SheetNumArray = new  Integer[split.length];
            for (int i = 0; i < split.length; i++) {
                SheetNumArray[i] = Integer.valueOf(split[i]);
            }
            result = ReadData(workbook, StatrRow, EndRow, ExistTop, SheetNumArray);
        }
        return result;
    }


    /**
     *  HandleData函数数据读取实际操作的抽离部分
     * */
    private static List<List<Map<String, Object>>> ReadData(Workbook workbook, int StatrRow, int EndRow, boolean ExistTop,Integer[] SheetNumArray){
        //声明返回结果集result
        List<List<Map<String, Object>>> result = new ArrayList<>();
        //声明一个Excel头部函数
        ArrayList<String> top = new ArrayList<>();
        for (int sheetNum = 0; sheetNum < SheetNumArray.length; sheetNum++) {
            //声明中间返回结果集result
            List<Map<String, Object>> middleResult = new ArrayList<>();
            Sheet sheet = workbook.getSheetAt(SheetNumArray[sheetNum]);
            // 校验sheet是否合法
            if (sheet == null) {
                continue;
            }
            //如存在头部,处理头部数据
            if (ExistTop) {
                int firstRowNum = sheet.getFirstRowNum();
                Row firstRow = sheet.getRow(firstRowNum);
                if (null == firstRow) {
                    logger.warn("解析Excel失败,在第一行没有读取到任何数据!");
                }
                for (int i = 0; i < firstRow.getLastCellNum(); i++) {
                    top.add(convertCellValueToString(firstRow.getCell(i)));
                }
            }
            //处理Excel数据内容
            int endRowNum;
            //获取结束行数
            if (EndRow == -1) {
                endRowNum = sheet.getPhysicalNumberOfRows();
            } else {
                endRowNum = EndRow <= sheet.getPhysicalNumberOfRows() ? EndRow : sheet.getPhysicalNumberOfRows();
            }
            //遍历行数
            for (int i = StatrRow - 1; i < endRowNum; i++) {
                Row row = sheet.getRow(i);
                if (null == row) {
                    continue;
                }
                Map<String, Object> map = new HashMap<>();
                //获取所有列数据
                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)));
                    }
                }
                middleResult.add(map);
            }
            result.add(middleResult);
        }
        return result;
    }

    /**
     * 方法一
     * 根据行数和列数读取Excel
     * fileName:Excel文件路径
     * StatrRow:读取的开始行数(默认填0)
     * EndRow:读取的结束行数(填-1为全部)
     * ExistTop:是否存在头部(如存在则读取数据时会把头部拼接到对应数据,若无则为当前列数)
     * SheetNum:Excel工作表页数,以,分割,全选为-1(从0开始的整数,就是Excel下面那个sheet)
     * 返回一个List<List<Map<String,Object>>>
     */
    public static List<List<Map<String, Object>>> ReadExcelByRC(String fileName, int StatrRow, int EndRow, boolean ExistTop ,String SheetNum) {
        //判断输入的开始值是否大于等0
        if(StatrRow < 0){
            logger.warn("输入的开始行值小于0,请重新输入");
            return null;
        }
        //判断输入的开始值是否少于等于结束值
        if (StatrRow > EndRow && EndRow != -1) {
            logger.warn("输入的开始行值比结束行值大,请重新输入正确的行数");
            return null;
        }
        if (SheetNum==null||SheetNum.isEmpty()||"".equals(SheetNum)){
            logger.warn("请输入需要读取的Sheet页数,页数从0开始");
            return null;
        }
        //声明返回的结果集
        List<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内容
            result = HandleData(workbook, StatrRow, EndRow, ExistTop,SheetNum);
        } 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("关闭数据流出错!错误信息:" + e.getMessage());
                return null;
            }
        }
        return result;
    }


/**==============================================================================================================================**/

    /**
     * 方法二
     * 根据给定的实体类中赋值的注解值读取Excel
     * fileName:Excel文件路径
     * StatrRow:读取的开始行数(默认填0)
     * EndRow:读取的结束行数(填-1为全部)
     * Class<T>:传过来的实体类类型
     * SheetNum:Excel工作表页数,以,分割,全选为-1(从0开始的整数,就是Excel下面那个sheet)
     * 返回一个List<List<T>>:T为实体类
     */
    public static List<List<Object>> ReadExcelByPOJO(String fileName, int StatrRow, int EndRow, Class<?> t , String SheetNum) throws InvocationTargetException, IntrospectionException, InstantiationException, IllegalAccessException, NoSuchFieldException {
        //判断输入的开始值是否少于等于结束值
        if (StatrRow > EndRow && EndRow != -1) {
            logger.warn("输入的开始行值比结束行值大,请重新输入正确的行数");
            return null;
        }
        //声明返回的结果集
        List<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内容
            result = HandleDataPOJO(workbook, StatrRow, EndRow, t , SheetNum);
        } 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("关闭数据流出错!错误信息:" + e.getMessage());
                return null;
            }
        }
        return result;
    }


    /**
     * 处理Excel内容转为List<T>输出
     * workbook:已连接的工作薄
     * StatrRow:读取的开始行数(默认填0,0开始,传过来是EXcel的行数值默认从1开始,这里已处理减1)
     * EndRow:读取的结束行数(填-1为全部)
     * Class<T>:所映射的实体类
     */
    private static <t> List<List<Object>> HandleDataPOJO(Workbook workbook, int StatrRow, int EndRow, Class<?> t , String SheetNum) throws IllegalAccessException, IntrospectionException, InstantiationException,  InvocationTargetException, NoSuchFieldException {
        //声明返回结果集result
        List<List<Object>> result = new ArrayList<>();
        //解析sheet(sheet是Excel脚页)
        if (SheetNum =="-1"){
            Integer[] SheetNumArray = new  Integer[workbook.getNumberOfSheets()];
            for (int i = 0; i < workbook.getNumberOfSheets(); i++) {
                SheetNumArray[i] = i;
            }
            result = ReadDataPOJO(workbook, StatrRow, EndRow, t, SheetNumArray);
        }else {
            String[] split = SheetNum.split(",");
            Integer[] SheetNumArray = new  Integer[split.length];
            for (int i = 0; i < split.length; i++) {
                SheetNumArray[i] = Integer.valueOf(split[i]);
            }
            result = ReadDataPOJO(workbook, StatrRow, EndRow, t, SheetNumArray);
        }
        return result;
    }

    /**
     * HandleDataPOJO函数数据读取实际操作的抽离部分
     * */
    private static <t> List<List<Object>> ReadDataPOJO(Workbook workbook, int StatrRow, int EndRow, Class<?> t,Integer[] SheetNum)throws IntrospectionException, NoSuchFieldException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //声明返回的结果集
        List<List<Object>> result = new ArrayList<>();
        //解析sheet(sheet是Excel脚页)
        for (int sheetNum = 0; sheetNum < SheetNum.length; sheetNum++) {
            //声明返回的中间结果集
            List<Object> middleResult = new ArrayList<Object>();
            Sheet sheet = workbook.getSheetAt(SheetNum[sheetNum]);
            // 校验sheet是否合法
            if (sheet == null) {
                continue;
            }
            //获取头部数据
            //声明头部数据数列对象
            ArrayList<String> top = new ArrayList<>();
            //获取Excel第一行数据
            int firstRowNum = sheet.getFirstRowNum();
            Row firstRow = sheet.getRow(firstRowNum);
            if (null == firstRow) {
                logger.warn("解析Excel失败,在第一行没有读取到任何数据!");
                return null;
            }
            for (int i = 0; i < firstRow.getLastCellNum(); i++) {
                top.add(convertCellValueToString(firstRow.getCell(i)));
            }
            //获取实体类的成原变量
            Map<String, Object> POJOfields = getPOJOFieldAndValue(t);
            //判断所需要的数据列
            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数据内容*/
            int endRowNum;
            //获取结束行数
            if (EndRow == -1) {
                endRowNum = sheet.getPhysicalNumberOfRows();
            } else {
                endRowNum = EndRow <= sheet.getPhysicalNumberOfRows() ? EndRow : sheet.getPhysicalNumberOfRows();
            }
            List<Map<String, Object>> mapList = new ArrayList<>();
            //遍历行数
            for (int i = StatrRow - 1; i < endRowNum; i++) {
                Row row = sheet.getRow(i);
                if (null == row) {
                    continue;
                }
                //获取需要的列数据
                t texcel = (t) t.newInstance();
                for (Map.Entry<String, Object> map : exceltoPOJO.entrySet()) {
                    //获取Excel对应列的数据
                    String celldata = convertCellValueToString(row.getCell(Integer.parseInt(map.getKey())));  
                    //获取实体类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")) {
                        if (celldata!=null&&!"".equals(celldata)&&!celldata.isEmpty()){
                            //执行set方法
                            method.invoke(texcel, celldata);
                        }
                        else {/*什么都不用做,甚至这个else都可以不要,因为实体类初始化时会给对应的成员变量赋予空值 */}
                    }
                    if (object.endsWith("Double")) {
                        System.out.println(celldata);
                        if (celldata!=null&&!"".equals(celldata)&&!celldata.isEmpty()) {
                            Double middata = Double.valueOf(celldata);
                            System.out.println("middata:"+middata);
                            //执行set方法
                            method.invoke(texcel, middata);
                        }
                        else {/*什么都不用做,甚至这个else都可以不要,因为实体类初始化时会给对应的成员变量赋予空值 */}
                    }
                    if (object.endsWith("Float")) {
                        if (celldata!=null&&!"".equals(celldata)&&!celldata.isEmpty()){
                            Float middata = Float.valueOf(celldata);
                            //执行set方法
                            method.invoke(texcel, middata);
                        }
                        else {/*什么都不用做,甚至这个else都可以不要,因为实体类初始化时会给对应的成员变量赋予空值 */}
                    }
                    if (object.endsWith("Integer")) {
                        if (celldata!=null&&!"".equals(celldata)&&!celldata.isEmpty()){
                            Integer  middata = Integer.parseInt(celldata);
                            //执行set方法
                            method.invoke(texcel, middata);
                        }
                        else {/*什么都不用做,甚至这个else都可以不要,因为实体类初始化时会给对应的成员变量赋予空值 */}
                    }
                }
                middleResult.add(texcel);
            }
            result.add(middleResult);
        }
        return result;
    }

    /**
     * 获取对应的实体类成员
     * */
    private static Map<String, Object> getPOJOFieldAndValue(Class T) {
        //声明返回结果集
        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.info("实体类:" + T + "不存在成员变量");
            return null;
        }
        return result;
    }

}

测试类说明

  1. 返回的类型为List<List<?>>,外层List是表的页数,内层List才是表内的数据
  2. 最后的参数SheetNum:Excel工作表页数,以,分割,全选为-1(从0开始的整数,就是Excel下面那个sheet)
  3. 其他与以前一样,没有改动

在这里插入图片描述
在这里插入图片描述


还有很多不足的地方,请多多指点,希望能给你带来帮助。

  • 44
    点赞
  • 228
    收藏
    觉得还不错? 一键收藏
  • 39
    评论
Spring Boot可以使用EasyExcel实现Excel导入数据。EasyExcel是阿里基于poi开源的一个项目,它可以帮助我们更方便地实现Excel的导入与导出功能。在Spring Boot中使用EasyExcel,我们可以综合应用各种Spring知识,代码量并不大。首先,在Controller层,我们可以提供一个访问接口,通过POST请求方式传入Excel文件。在请求中,我们需要将文件一同传入,并获取文件名用于后续判断是否为Excel文件。然后,我们可以调用Service层的batchImport方法进行业务逻辑处理。在Service层,我们可以使用EasyExcel提供的API来读取Excel文件中的数据,并进行相应的处理。最后,我们可以返回处理结果给前端。这样,就可以实现Spring Boot中的Excel导入数据功能。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* *3* [SpringBoot 注解 + 反射优雅的实现Excel导入导出,通用加强版!](https://blog.csdn.net/afreon/article/details/126756870)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [SpringBoot提供接口实现Excel导入数据并存储到数据库中](https://blog.csdn.net/m0_51197424/article/details/124454553)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 39
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值