前言:在我们的工作当中,时常会有将Excel文档进行导入导出的操作,针对于此项需求,有很多的处理方法。而我们今天就讲一下其中最为常用的POI。

1.POI介绍

POI是Apache软件基金会的开放源码函式库。
POI提供API给Java程序对Microsoft Office格式档案读和写的功能。

2.Excel介绍

Excel文档有两种格式,分别是

  • 03版的excel,文件后缀为.xls (最多能容纳65536行数据)
  • 07版的excel,文件后缀为.xlsx(最多能容纳1048576行数据)

3.导入依赖

导入poi依赖,可以根据需求导入相对应的依赖。
poi可以操作03版的excel,后缀为.xls
poi-ooxml可以操作07版的excel,后缀为.xlsx

<!--03版excel  后缀为.xls-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi</artifactId>
        <version>3.9</version>
    </dependency>

    <!--07版excel  后缀为.xlsx-->
    <dependency>
        <groupId>org.apache.poi</groupId>
        <artifactId>poi-ooxml</artifactId>
        <version>3.14</version>
    </dependency>

4.认识类与方法

  • HSSFWorkbook:一般用于操作03版以前的excel(包括03版excel),扩展名是.xls。
  • XSSFWorkbook:一般用于操作07版excel及以上的版本,扩展名是.xlsx。
  • SXSSFWorkbook(POI 3.8+版本):一般用于大数据量的导出。比如数据量超过5000条即可考虑这种工作表

这三个类可以根据需求来进行选择。
下面介绍一下三个类共有的方法。了解了这些方法,就可以更加快捷的去导入导出Excel

//获取工作薄
 Workbook workbook=new HSSFWorkbook();
  //获取工作表
  Sheet sheet = workbook.createSheet();
  //生成第一行 参数为行的下标,下标从0开始
  Row row = sheet.createRow(0);
  //生成第一列 参数为列的下标,下标从0开始
  Cell cell1 = row.createCell(0);
  //给第一行第一列的单元格赋值
  cell1.setCellValue("姓名");
  //输出流导出文档
  FileOutputStream fileOutputStream = new FileOutputStream(new File(PATH + "hhh.xls"));
   workbook.write(fileOutputStream);
//读取excel文档 生成工作薄对象
FileInputStream fileInputStream = new FileInputStream(new File("D:\\Users\\idea\\test\\lilun\\excel\\srchhh.xls"));
HSSFWorkbook hssfWorkbook = new HSSFWorkbook(fileInputStream);
//读取第一个工作表  参数为工作表下标 下标从0开始
HSSFSheet sheetAt = hssfWorkbook.getSheetAt(0);
//读取工作表第一行数据  参数为行下标 下标从0开始
HSSFRow row = sheetAt.getRow(0);
//一行有几列 返回列数
int num = row.getPhysicalNumberOfCells();
//根据行取对应的列  获取单元格  参数为行下标 下标从0开始
HSSFCell cell = row.getCell(0);
//获取单元格的数据类型 返回值是一个int类型
int cellType = cell.getCellType();

获取类型后就可以进行判断
CELL_TYPE_NUMERIC 数值型 0
CELL_TYPE_STRING 字符串型 1
CELL_TYPE_FORMULA 公式型 2
CELL_TYPE_BLANK 空值 3
CELL_TYPE_BOOLEAN 布尔型 4
CELL_TYPE_ERROR 错误 5
根据数据类型的不同采用不同的方法获取单元格内的值

//判断单元格数据类型
  switch (cellType){
      case 0:
          //单元格返回double类型
          double numericCellValue = cell.getNumericCellValue();
          break;
      case 1:
          //单元格返回string字符串类型
          String stringCellValue = cell.getStringCellValue();
          break;
      case 2:
          //单元格返回公式
          String cellFormula = cell.getCellFormula();
          break;
      case 3:
          //单元格为空
          break;
      case 4:
          //单元格返回boolean类型
          boolean booleanCellValue = cell.getBooleanCellValue();
          break;
      case 5:
          //单元格返回错误
          byte errorCellValue = cell.getErrorCellValue();
          break;
          default:
              break;
  }

5.读取Excel的方法

/**
     * 读取Excel文档
     * @throws IOException
     */
    @Test
    public void inExcel() throws IOException {

        //读取excel文档 生成工作薄对象
        FileInputStream fileInputStream = new FileInputStream(new File("D:\\Users\\idea\\test\\lilun\\excel\\srchhh.xls"));
        HSSFWorkbook hssfWorkbook = new HSSFWorkbook(fileInputStream);

        //读取第一个工作表  参数为工作表下标 下标从0开始
        HSSFSheet sheetAt = hssfWorkbook.getSheetAt(0);
        //读取工作表第一行数据  参数为行下标 下标从0开始
        HSSFRow row = sheetAt.getRow(0);

        if(row!=null){
            //一行有几列 返回列数
            int num = row.getPhysicalNumberOfCells();
            for (int i=0;i<num;i++){
                //根据行取对应的列  获取单元格  参数为行下标 下标从0开始
                HSSFCell cell = row.getCell(i);
               if(cell!=null){
                   //获取单元格的数据类型
                   int cellType = cell.getCellType();
                   //判断单元格数据类型
                   switch (cellType){
                       case 0:
                           //单元格返回double类型
                           double numericCellValue = cell.getNumericCellValue();
                           break;
                       case 1:
                           //单元格返回string字符串类型
                           String stringCellValue = cell.getStringCellValue();
                           break;
                       case 2:
                           //单元格返回公式
                           String cellFormula = cell.getCellFormula();
                           break;
                       case 3:
                           //单元格为空
                           break;
                       case 4:
                           //单元格返回boolean类型
                           boolean booleanCellValue = cell.getBooleanCellValue();
                           break;
                       case 5:
                           //单元格返回错误
                           byte errorCellValue = cell.getErrorCellValue();
                           break;
                           default:
                               break;
                   }

               }
            }
        }
        fileInputStream.close();


    }

6.生成Excel的方法

String PATH="D:\\Users\\idea\\test\\lilun\\excel\\";

    /**
     * 生成Excel文档
     * @throws IOException
     */
    @Test
    public void toExcel() throws IOException {
        //获取工作薄
        Workbook workbook=new HSSFWorkbook();
        //获取工作表
        Sheet sheet = workbook.createSheet();
        //生成第一行 参数为行的下标,下标从0开始
        Row row = sheet.createRow(0);
        //生成第一列 参数为列的下标,下标从0开始
        Cell cell1 = row.createCell(0);
        //给第一行第一列的单元格赋值
        cell1.setCellValue("姓名");
        Cell cell2 = row.createCell(1);
        cell2.setCellValue("性别");
        Row row1 = sheet.createRow(1);
        Cell cell11 = row1.createCell(0);
        cell11.setCellValue("hh");
        Cell cell22 = row1.createCell(1);
        cell22.setCellValue("man");
        //输出流导出文档
        FileOutputStream fileOutputStream = new FileOutputStream(new File(PATH + "hhh.xls"));
        workbook.write(fileOutputStream);
        fileOutputStream.close();

    }

如果想要更加符合真实的逻辑,与前台数据库交互,那就可以借助工具类。
下面小编将会举一个案例,来使用poi进行导入导出Excel

7.导入导出POI工具类

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.util.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.sql.DataSource;
import java.io.*;
import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @Description: TODO
 * @author: sakura
 * @date: 2020/6/20 17:18
 * @version: V1.0
 **/
public class PoiUtil {



        /**
         * 导出到excel中
         * @param connection  与数据库的连接
         * @param sql  查询的sql语句 select * from 表名
         * @param tableName excel的sheet名
         * @param filePath  输出的文件路径
         */
        public static void exportToExcel(Connection connection, String sql, String tableName, String filePath){

            ResultSet resultSet = null;
            PreparedStatement statement = null;
            OutputStream os=null;

            //创建一个excel工作簿
            HSSFWorkbook workbook = new HSSFWorkbook();

            //创建表 并设置sheet的名称
            HSSFSheet sheet = workbook.createSheet(tableName);

            //创建标题行
            HSSFRow row = sheet.createRow(0);

            //日期格式化
            HSSFCellStyle cellStyle = workbook.createCellStyle();
            HSSFCreationHelper creationHelper = workbook.getCreationHelper();
            cellStyle.setDataFormat(creationHelper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));

            try {

                //通过传进来的sql 查询表中的数据
                statement = connection.prepareStatement(sql);
                //获取结果集
                resultSet = statement.executeQuery();
                //获取元数据    用来获取字段名
                ResultSetMetaData metaData = resultSet.getMetaData();
                //每一行的列数
                int columnCount = metaData.getColumnCount();

                //动态根据字段名设置列名 即标题
                for (int i = 0; i < columnCount; i++) {
                    String labelName = metaData.getColumnLabel(i + 1);
                    row.createCell(i).setCellValue(labelName);
                }

                int i=1;

                //临时行
                HSSFRow tempRow;
                //遍历结果集  往excel中写数据
                while (resultSet.next()){
                    //创建临时行  即当前行
                    tempRow = sheet.createRow(i);
                    for (int j = 0; j < columnCount; j++) {
                        //获取当前单元格
                        HSSFCell tempCell = tempRow.createCell(j);
                        //获取数据库中与当前格对应的数据
                        Object temp=resultSet.getObject(j+1);

                        //如果获取到的数据为空则跳过该单元格
                        if (temp==null || "".equals(temp)){
                            continue;
                        }

                        //如果获取到的是时间  则格式化时间再写到excel中
                        if (temp instanceof java.util.Date){
                            //给日期设置样式
                            tempCell.setCellStyle(cellStyle);
                            tempCell.setCellValue((java.util.Date) temp);
                        }else if(temp instanceof Boolean){
                            tempCell.setCellValue((Boolean) temp);
                        }else if (temp instanceof Double){
                            tempCell.setCellValue((Double) temp);
                        }else {
                            tempCell.setCellValue(temp.toString());
                        }
                    }
                    i++;
                }

                os=new BufferedOutputStream(new FileOutputStream(filePath));
                //将excel表格写出到指定的路径下
                workbook.write(os);
                System.out.println(filePath+"导出成功");

            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                //关闭资源
                IOUtils.closeQuietly(os);
                if (resultSet!=null){
                    try {
                        resultSet.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
                if (connection!=null){
                    try {
                        connection.close();
                    } catch (SQLException e) {
                        e.printStackTrace();
                    }
                }
            }
        }




    /**
     *
     * 从excel导入到mysql中
     * @param t 与数据库中要插入的表对应的实体类
     * @param file   excel文件流
     * @param <T>
     * @return  实体类的list集合
     */
    public static <T> List<T> importToMysql(Class<T> t, InputStream file){

        //准备一个list用来存放结果
        List<T> list=new ArrayList<>();

        HSSFWorkbook workbook= null;
        try {
            //通过文件路径创建一个工工作簿
            workbook = new HSSFWorkbook(file);
        } catch (IOException e) {
            System.out.println("文件读取失败");
            System.out.println(e.getMessage());
        }

        //获取当前excel的第一个sheet表格  如果有多个sheet就自行遍历
        HSSFSheet sheet = workbook.getSheetAt(0);

        //获取excel表的第一行   用来获取表的字段名
        HSSFRow tempRow = sheet.getRow(0);

        //获取总的行数
        int lastRowNum = sheet.getLastRowNum();

        //遍历行    从第二行开始   第一行为表的字段名
        for (int i = 1; i <= lastRowNum; i++) {
            HSSFRow row = sheet.getRow(i);

            //获取列数
            int cells = row.getPhysicalNumberOfCells();

            //实例化实体类对象
            T tempT= null;
            try {
                tempT = t.newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

            //遍历当前行的每一列
            for (int j = 0; j < cells; j++) {
                //当前列的值
                HSSFCell hsscell = row.getCell(j);
                //当前列的名称
                HSSFCell cellName= tempRow.getCell(j);

                Field field = null;
                try {
                    //根据列名获取实体类与之对应的属性
                    field = tempT.getClass().getDeclaredField(cellName.toString());
                } catch (NoSuchFieldException e) {
                    System.out.println(field.getName()+"属性获取失败");
                    System.out.println(e.getMessage());
                }

                //如果该字段为空  则跳过
                if (hsscell==null){
                    continue;
                }

                try {
                    //根据从excel获取到的数据的类型设值
                    if (field!=null){
                        //私有属性授权
                        field.setAccessible(true);
                        if (hsscell.getCellTypeEnum()== CellType.NUMERIC){
                            if(HSSFDateUtil.isCellDateFormatted(hsscell)){//日期
                                if (field.getType()== Date.class){
                                    field.set(tempT,hsscell.getDateCellValue());
                                }
                            }else if(field.getType()==Integer.class){
                                //int类型
                                field.set(tempT,Integer.valueOf(hsscell.getStringCellValue()));
                            }else if(field.getType()==Double.class){
                                //double类型
                                field.set(tempT,Double.parseDouble(hsscell.toString()) );
                            }
                        }else if (hsscell.getCellTypeEnum()==CellType.BOOLEAN){
                            //布尔值
                            if (field.getType()==Boolean.class){
                                field.set(tempT,hsscell.getBooleanCellValue() );
                            }
                        }else if(hsscell.getCellTypeEnum()==CellType.STRING){
                            if (field.getType()==Integer.class){
                                field.set(tempT,Integer.parseInt(hsscell.toString()));
                            }else if (field.getType()==Double.class){
                                field.set(tempT,Double.valueOf(hsscell.toString()) );
                            }else if (field.getType()==String.class){
                                field.set(tempT,hsscell.toString() );
                            }else if(HSSFDateUtil.isCellDateFormatted(hsscell)){
                                if (field.getType()== Date.class){
                                    field.set(tempT,hsscell.getDateCellValue());
                                }
                            }
                        }
                    }
                } catch (IllegalAccessException e) {
                    System.out.println(field.getName()+"设值失败");
                    e.printStackTrace();
                }
            }
            //添加到list集合中
            list.add(tempT);
        }
        //将封装好的list集合返回
        return list;
    }
}

8.控制层

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author LH
 * @since 2020-06-04
 */
@RestController
@RequestMapping("/user")
public class UserController {


    @Autowired
    private UserDao userDao;


    /**
     * 导出excel
     * @param size导出数据条数
     * @param name 文件的名称
     * @return
     */
    @RequestMapping("toExcel")
    public ResponseResult toExcel(Integer size,String name){
        ResponseResult responseResult = ResponseResult.getResponseResult();
        try {
            //通过数据源获取与数据库的连接  没配置spring的可用原生的jdbc来获取连接
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/user?serverTimezone=UTC", "root", "root");
            //查询的sql语句
            StringBuilder sql=new StringBuilder("select * from base_user");
            if(size!=null && size!=0){
                sql.append(" limit 0,"+size);
            }
            //生成的excel表的路径   注意文件名要和数据库中表的名称一致  因为导入时要用到。
            String filePath="D:\\Desktop\\"+name+".xls";
            //导出
            PoiUtil.exportToExcel(connection,sql.toString(), "base_user", filePath);
            responseResult.setCode(200);
            responseResult.setSuccess("导出excel成功");
            return responseResult;
        }catch (Exception x){
            x.printStackTrace();
            responseResult.setCode(500);
            responseResult.setSuccess("导出excel失败");
            return responseResult;
        }

    }

    @Autowired
    private KafkaTemplate kafkaTemplate;

    /**
     * 导入excel
     * @param file
     * @return
     */
    @RequestMapping("insertExcel")
    public ResponseResult insertExcel(@RequestParam("file") MultipartFile file){
        ResponseResult responseResult = ResponseResult.getResponseResult();
        try {

            List<User> users = PoiUtil.importToMysql(User.class, file.getInputStream());
          
            userDao.insertUser(users);
            responseResult.setCode(200);
            responseResult.setSuccess("导入excel成功");
            return responseResult;
        }catch (Exception x){
            x.printStackTrace();
            responseResult.setCode(500);
            responseResult.setSuccess("导入excel失败");
            return responseResult;
        }

    }


}

9.实体类

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

/**
 * @Description: user实体类
 * @author: sakura
 * @date: 2020/6/6 8:30
 * @version: V1.0
 **/
@Data
public class User implements Serializable {

    private Integer id;


    private Integer version;


    private String userName;


    private String passWord;


    private String loginName;

    private String email;

    private String tel;

    private String buMen;

    private String salt;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

    private String start;

    private String end;


    private String img;


    private Integer sex;

    private Integer activate;


}

10.数据访问层

此案例运用mybatis进行数据持久化

/**
 * @Description: user数据访问层
 * @author: sakura
 * @date: 2020/6/4 14:33
 * @version: V1.0
 **/
@Mapper
public interface UserDao {

    public void insertUser(List<User> user);
}

11.mapper

<insert id="insertUser">
        insert into base_user(version,userName,loginName,passWord,email,tel,buMen,salt,createTime,updateTime,img,sex,activate) values
        <foreach collection="list" item="u" separator=",">
            (0,#{u.userName},#{u.loginName},'DE2E529EBBBDE7EB08B63B2452B9802E',#{u.email},#{u.tel},'实习生','cascade',#{u.createTime},#{u.updateTime},'http://106.12.200.44:9000/test01/222.jpg',#{u.sex},0)
        </foreach>
    </insert>

到这里,案例就展示完毕了。
如果有任何不足,还请大佬指正。