前言
实习期间遇到一个业务需求,要求具有excel导入和导出功能。由于公司框架的数据表格自带了excel导出功能,所以只需excel导入即可,流程是这样的:选择excel →文件上传→处理数据。
项目使用的Jar包是Apache POI,关于Apache POI,简单介绍一下:
Apache POI是基于Office Open XML标准(OOXML)和Microsoft的OLE2复合文档格式(OLE2)处理各种文件格式的开源项目。简言之,就是用来操作office的工具箱。
其中操作Excel的模块有两个:
- HSSF - 操作xls格式excel(低版本),有数据量限制
- XSSF - 操作xlsx格式excel(高版本),支持百万级数据量
1、工作准备
打开maven仓库:https://mvnrepository.com/,输入POI,如下图:
点进去,推荐选择的版本特点——时间不早不晚,多人使用的(遇到问题降低了解决难度),如下图:
这里选择3.17版本(beta可以理解为测试、抢先,通常是有新功能或大的调整),点击,复制maven坐标,如下图:
poi-ooxml如是,然后把它们贴入pom.xml,如下图:
然后在idea中右键选中:Maven→Reimport,或者点击enable auto import,让maven把jar包接入项目即可。
2、小试牛刀
由于excel存在版本区别,需要分别处理xls和xlsx格式,分别对应HSSF、XSSF开头的API,按住CTRL + SHIFT + ALT + U,查看继承关系图,可以发现它们有相同的接口:
其它类,如:HSSFSheet、XSSFSheet也可以通过Sheet接口引用(里式替换法则),测试代码如下(xlxs):
private static void parseXlsxDemo() throws Exception {
// File类:以抽象的方式代表文件名和目录路径名
File file = new File("d:\\good.xls");
// 建立文件输入流
FileInputStream fis = new FileInputStream(file);
// 声明并创建一个工作簿
HSSFWorkbook workbook = new HSSFWorkbook(fis);
// sheet表示文件页,下标从0开始,表示第一页
HSSFSheet sheet = workbook.getSheetAt(0);
// 获取总行数(第一页)
int rows = sheet.getPhysicalNumberOfRows();
for (int i = 0; i < rows; i++) {
// 先获取行对象,再操作列
Row row = sheet.getRow(i);
// 获取每一行的格子数(列数)
int cols = row.getPhysicalNumberOfCells();
for (int j = 0; j < cols; j++) {
// 获取格子对象
Cell cell = row.getCell(j);
// 输出格子对象
System.out.printf("%s\t", cell.toString());
}
// 换行输出
System.out.println();
}
}
文件截图+运行效果:
细心的朋友会发现,0变成了0.0,1变成了1.0,这是因为POI默认数字为浮点数。数据类型稍微有点复杂,此处不深究,下面是修改后的完整代码:
package ltd.newson.poi;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
/**
* @Title: ReadExcel
* @Author: newson.ltd
* @Date: 2021-01-26 下午 12:01
* @Version: 1.0
* @Description: excel demo
*/
public class ReadExcel {
public static void main(String[] args) throws Exception {
String fileName1 = "d:\\good.xls";
String fileName2 = "d:\\good.xlsx";
parseXls(fileName1);
parseXlsx(fileName2);
}
/**
* 读取xls格式excel
* @param fileName
* @throws Exception
*/
private static void parseXls(String fileName) throws Exception {
// 1、读取文件(逐层包装,装饰器模式)
FileInputStream fis = new FileInputStream(new File(fileName));
Workbook workbook = new HSSFWorkbook(fis);
// 2、获取要处理的页面
Sheet sheet = workbook.getSheetAt(0);
// 3、处理单元格
printCell(sheet);
}
/**
* 读取xlsx格式excel
* @param fileName
* @throws Exception
*/
private static void parseXlsx(String fileName) throws Exception {
FileInputStream fis = new FileInputStream(new File(fileName));
Workbook workbook = new XSSFWorkbook(fis);
Sheet sheet = workbook.getSheetAt(0);
printCell(sheet);
}
/**
* 打印单元格
* @param sheet
*/
private static void printCell(Sheet sheet) {
// 获取当前页的总行数
int totalRowNums = sheet.getPhysicalNumberOfRows();
for (int i = 0; i < totalRowNums; i++) {
// 获取行
Row row = sheet.getRow(i);
// 获取列数(格子数)
int cells = row.getPhysicalNumberOfCells();
StringBuilder builder = new StringBuilder();
for (int j = 0; j < cells; j++) {
Cell cell = row.getCell(j);
builder.append(getCellValue(cell)).append("\t");
}
System.out.print(builder.append("\n").toString());
}
}
/**
* 获取单元格的值,此处做简单处理
* @param cell
* @return
*/
private static String getCellValue(Cell cell) {
// 部分API已过时,此处是更新后的
CellType cellType = cell.getCellTypeEnum();
if (cellType == CellType.BOOLEAN) {
return String.valueOf(cell.getBooleanCellValue());
}
if (cellType == CellType.NUMERIC) {
return cell.getNumericCellValue() + "";
}
return cell.getStringCellValue();
}
}
运行截图:
补充:注意getCellValue()中的API,有些教程可能会使用cell.getCellType()和Cell.CELL_TYPE_XXX(XXX表示数据类型),这些方法在新的POI中被标记为@deprecated(过时的),建议换成上述方法内的API。
3、小结
通过代码+注释的解读,会发现excel的读取是这么的简单,归纳一下操作步骤:
- 输入目标文件,获取wordbook对象(工作簿)
- 通过workbook对象获取sheet对象(要操作的页面)
- 通过sheet对象获取当前页的总行数
- 开循环,获取行对象,并通过行获取每一行的列数(格子数)
- 再开个循环,通过行对象获取每一个格子对象
- 操作格子对象(注意数据类型)
最后一步也是比较复杂的一步,需要具体情况具体分析。本示例操作单页,多页同理。最后扩展一下excel单元格的数据类型:
CellType | 含义 |
---|---|
_NONE | 未知类型 |
NUMERIC | 数值类型(整数、小数、日期) |
STRING | 字符串 |
FORMULA | 公式 |
BLANK | 空白格(有样式) |
BOOLEAN | 布尔值 |
ERROR | 错误单元格 |
写在最后
本次Java读取excel就介绍到这里啦,我是严光君,咱们下文再见~
创造不易,少侠请留步…… 动起可爱的双手,点个赞再走呗~ ٩(๑>◡<๑)۶