气死了,之前遇到一个感觉比较好用的poi导入,谁知道现在用起来,各种bug,干脆使用最新的api模仿实现了一个,使用模版方法模式回调,只需要实现接口,处理每一行数据,就ok了。此实现只支持单页sheetIndex
多页实现请参考:http://git.oschina.net/zhuqiang/utils
使用到的jar包pom
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.12</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.12</version>
</dependency>
核心PoiAbstract抽象类
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DateUtil;
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.ss.usermodel.WorkbookFactory;
/**
* 读取xlsx 和 xls,只支持单页sheetIndex
* @author zq
*
*/
public abstract class PoiAbstract {
Workbook wb = null;
public PoiAbstract(String path) {
try {
InputStream inp = new FileInputStream(path);
wb = WorkbookFactory.create(inp); //使用工厂创建,兼容xlsx 和 xls
} catch (Exception e) {
new RuntimeException(e.getMessage());
}
}
/**
* 遍历Excel所有数据,包含header
*
* @param sheetIndex
*/
public void process(int sheetIndex) {
int columnNum = 0; // 获取列
Sheet sheet = wb.getSheetAt(sheetIndex);
if (sheet.getRow(0) != null) {
columnNum = sheet.getRow(0).getLastCellNum() - sheet.getRow(0).getFirstCellNum();
}
if (columnNum > 0) {
for (Row row : sheet) {
List<String> rowlist = new ArrayList<String>(columnNum);
int n = 0;
for (int i = 0; i < columnNum; i++) {
Cell cell = row.getCell(i, Row.CREATE_NULL_AS_BLANK);
switch (cell.getCellType()) {
case Cell.CELL_TYPE_BLANK:
rowlist.add(n, "");
break;
case Cell.CELL_TYPE_BOOLEAN:
rowlist.add(Boolean.toString(cell.getBooleanCellValue()));
break;
// 数值
case Cell.CELL_TYPE_NUMERIC:
if (DateUtil.isCellDateFormatted(cell)) {
rowlist.add(String.valueOf(cell.getDateCellValue()));
} else {
cell.setCellType(Cell.CELL_TYPE_STRING);
String temp = cell.getStringCellValue();
// 判断是否包含小数点,如果不含小数点,则以字符串读取,如果含小数点,则转换为Double类型的字符串
if (temp.indexOf(".") > -1) {
rowlist.add(String.valueOf(new Double(temp)).trim());
} else {
rowlist.add(temp.trim());
}
}
break;
case Cell.CELL_TYPE_STRING:
rowlist.add(cell.getStringCellValue().trim());
break;
case Cell.CELL_TYPE_ERROR:
rowlist.add("");
break;
case Cell.CELL_TYPE_FORMULA:
cell.setCellType(Cell.CELL_TYPE_STRING);
rowlist.add(cell.getStringCellValue());
if (rowlist.get(n) != null) {
rowlist.set(n, rowlist.get(n).replaceAll("#N/A", "").trim());
}
break;
default:
rowlist.add("");
break;
}
n++;
}
try {
this.optRows(sheetIndex, row.getRowNum(), rowlist); // 行结束,调用行,每行的数据
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public abstract void optRows(int sheetIndex, int curRow, List<String> rowlist) throws Exception;
}
PoiRead 简易处理方式
import java.util.ArrayList;
import java.util.List;
/**
* 一种感觉比较好的处理方式。信息量比较全。
* @author zq
*
*/
public class PoiRead extends PoiAbstract {
// 数据处理解析数据的接口
private PoiOptRowsInterface poiOptRowsInterface;
// 处理数据总数 (如果有总数包含总数)
private int optRows_sum = 0;
// 处理数据成功数量
private int optRows_success = 0;
// 处理数据失败数量
private int optRows_failure = 0;
// excel表格每列标题
private List<String> rowtitle;
// 失败数据
private List<List<String>> failrows;
// 失败原因
private List<String> failmsgs;
// 要处理数据所在的sheet索引,sheet索引从0开始
private int sheetIndex;
/**
* @param path
* 导入文件的物理路径
* @param sheetIndex
* 要读取数据所在sheet序号,从0开始
* @param poiOptRowsInterface
* 处理读取每一行数据的接口
*/
PoiRead(String path, int sheetIndex, PoiOptRowsInterface poiOptRowsInterface) {
super(path);
this.sheetIndex = sheetIndex;
this.poiOptRowsInterface = poiOptRowsInterface;
this.rowtitle = new ArrayList<String>();
this.failrows = new ArrayList<List<String>>();
this.failmsgs = new ArrayList<String>();
}
@Override
public void optRows(int sheetIndex, int curRow, List<String> rowlist) throws Exception {
// 将rowlist的长度补齐和标题一致
int k = rowtitle.size() - rowlist.size();
for (int i = 0; i < k; i++) {
rowlist.add(null);
}
if (sheetIndex == this.sheetIndex) {
optRows_sum++;
if (curRow == 0) {// 记录标题
rowtitle.addAll(rowlist);
} else {
// 接口返回的结果是导入数据的结果,有成功,有失败
String result = poiOptRowsInterface.optRows(sheetIndex, curRow, rowlist);
if (result != null && !result.equals(PoiOptRowsInterface.SUCCESS)) {
optRows_failure++;// 失败统计数加1
// 失败数据列表
failrows.add(new ArrayList<String>(rowlist));
failmsgs.add(result);
} else {
optRows_success++;
}
}
}
}
public int getOptRows_sum() {
return optRows_sum;
}
public void setOptRows_sum(int optRows_sum) {
this.optRows_sum = optRows_sum;
}
public int getOptRows_success() {
return optRows_success;
}
public void setOptRows_success(int optRows_success) {
this.optRows_success = optRows_success;
}
public int getOptRows_failure() {
return optRows_failure;
}
public void setOptRows_failure(int optRows_failure) {
this.optRows_failure = optRows_failure;
}
public List<String> getRowtitle() {
return rowtitle;
}
public void setRowtitle(List<String> rowtitle) {
this.rowtitle = rowtitle;
}
public List<List<String>> getFailrows() {
return failrows;
}
public void setFailrows(List<List<String>> failrows) {
this.failrows = failrows;
}
public List<String> getFailmsgs() {
return failmsgs;
}
public void setFailmsgs(List<String> failmsgs) {
this.failmsgs = failmsgs;
}
//测试读取
public static void main(String[] args) {
PoiRead poiRead = new PoiRead("C:/Users/99299/Desktop/读取测试.xlsx", 0, new Impl());
poiRead.process(0);
System.out.println("处理数据总数:" + poiRead.getOptRows_sum());
System.out.println("处理数据成功数量:" + poiRead.getOptRows_success());
System.out.println("处理数据失败数量:" + poiRead.getOptRows_failure());
System.out.println("excel表格每列标题:" + poiRead.getRowtitle());
System.out.println("失败数据:" + poiRead.getFailrows());
System.out.println("失败原因:" + poiRead.getFailmsgs());
}
}
PoiRead的回调接口PoiOptRowsInterface
import java.util.List;
/**
* 与 简易实现 PoiRead.java类配合使用的回调接口
* @author zq
*
*/
public interface PoiOptRowsInterface {
public static final String SUCCESS = "success";
/**
* 处理excel文件每行数据方法
*
* @param sheetIndex
* 为sheet的序号
* @param curRow
* 为行号
* @param rowlist
* 行数据
* @return success:返回 SUCCESS 或则 null 表示成功,否则为失败原因
* @throws Exception
*/
public String optRows(int sheetIndex, int curRow, List<String> rowlist) throws Exception;
}
使用示例
编写一个回调接口的实现类
编写该类的一些思路:这里回调给你的是,当前页数,当前处理的行,当前行的所有列数据
在构建该实现类的时候,可以加入各种与数据库的服务,和各种验证方法,比如:可以定义一些变量,把所要保留下来的数据存起来;在返回的字符串中,按照一定的格式记录错误的行和列,在前台jsp页面中就能做到把错误的列都飘红显示。等等。 这里就简单的打印下
import java.util.List;
public class Impl implements PoiOptRowsInterface {
public String optRows(int sheetIndex, int curRow, List<String> rowlist) throws Exception {
System.out.println( curRow + "行------------");
for (String st : rowlist) {
System.out.print(st + "\t");
}
System.out.println("");
return "xxxxxxxxxx";
}
}