- 项目需要进行导入优化的时候,因为之前用poi旧版本读取excel时效率比较慢,后来了解的poi的驱动模式后,准备用来改造导入方法.在大批量数据面前效率提升比较明显(几百几千行数据时效率提供微弱) .
- 在封装Excel 读取工具类时,对别人的代码进行了一定的包装(包装代码如下) ,只要继承该类 覆写操作行方法,就可以取到自己期望的属性.
- 后来发现因为Excel中有空值的时候,不能生成成对的标签,所以在读取含有空的单元格时 不能满足特定的需求. 所以对该类又进行了封装.
- 感兴趣的可以自己对excel2007生成xml文件进行阅读,发现规律修改下边的方法.或者可以直接下载
- https://download.csdn.net/download/x308561498/11011654
- 测试类:
import java.io.File;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
public class ImportExample {
public final static Logger logger=LoggerFactory.getLogger(ImportExample.class);
/**
读取内容 如下
姓名,分数,备注
张三,80,及格
李四,60,
王五,100,优秀
* @throws Exception
*/
//将excel数据读入JsonArray中
public static void main(String[] args) throws Exception {
File file =new File("F:/info.xlsx");
InfoExcelRead infoExcel=new InfoExcelRead();
infoExcel.setReadLength(3);//设置读取列
infoExcel.readExcel2007(file);
logger.info(new Gson().toJson(infoExcel.getJos()));
}
}
class InfoExcelRead extends ImportExcelMap{
private JsonArray jos=new JsonArray();
public JsonArray getJos() {
return jos;
}
public void setJos(JsonArray jos) {
this.jos = jos;
}
@Override
public void optRow(int sheetIndex, int curRow, Map<Integer, String> rowMap) {
if (curRow>0) { //跳过列头
JsonObject jo=new JsonObject();
jo.addProperty("name", rowMap.get(1));
jo.addProperty("score", rowMap.get(2));
jo.addProperty("remarks", rowMap.get(3));
jos.add(jo);
}
}
}
- 测试结果如下:
~未处理空格的Excel工具类
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.model.SharedStringsTable;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
public class ImportExcelUtil extends DefaultHandler {
private SharedStringsTable sst;
private String lastContents;
private boolean nextIsString;
private int sheetIndex = -1;
private List<String> rowlist = new ArrayList<String>();
private int curRow = 0;
private int curCol = 0;
private String col = "";
public void readExcel2007(byte[] bytes) throws Exception{
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
readExcel2007(byteArrayInputStream);
}
public void readExcel2007(String path) throws Exception{
OPCPackage pkg = OPCPackage.open(path,PackageAccess.READ);
readOneSheet(pkg);
}
public void readExcel2007(InputStream inputStream) throws Exception{
try {
OPCPackage pkg = OPCPackage.open(inputStream);
readOneSheet(pkg);
} catch (Exception e) {
e.printStackTrace();
}finally{
if (inputStream!=null) {
inputStream.close();
}
}
}
public void readExcel2007(File file)throws Exception{
OPCPackage pkg = OPCPackage.open(file,PackageAccess.READ);
readOneSheet(pkg);
}
/**
* 直接读取第一个工作簿
* @param path
*/
public void readOneSheet(OPCPackage pkg) throws Exception {
XSSFReader r = new XSSFReader(pkg);
SharedStringsTable sst = r.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
InputStream sheet = r.getSheet("rId1");
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource);
sheet.close();
}
/**
* 该方法自动被调用,每读一行调用一次,在方法中写自己的业务逻辑即可
* @param sheetIndex 工作簿序号
* @param curRow 处理到第几行
* @param rowList 当前数据行的数据集合
*/
public void optRow(int sheetIndex, int curRow, List<String> rowList) {
System.out.println("-------curRow----------"+curRow);
String temp = "";
for(String str : rowList) {
temp += str + "_";
}
System.out.println("-------curValue----------:"+temp);
}
/**
* XML解析器
* @param sst
* @return
* @throws SAXException
*/
public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException {
XMLReader parser = XMLReaderFactory
.createXMLReader("org.apache.xerces.parsers.SAXParser");
this.sst = sst;
parser.setContentHandler(this);
return parser;
}
/**
* 开始元素
*/
public void startElement(String uri, String localName, String name,
Attributes attributes) throws SAXException {
// c => 单元格
if (name.equals("c")) {
col = attributes.getValue("r");
// 如果下一个元素是 SST 的索引,则将nextIsString标记为true
String cellType = attributes.getValue("t");
if (cellType != null && cellType.equals("s")) {
nextIsString = true;
} else {
nextIsString = false;
}
}
// 置空
lastContents = "";
}
public void endElement(String uri, String localName, String name)
throws SAXException {
// 根据SST的索引值的到单元格的真正要存储的字符串
// 这时characters()方法可能会被调用多次
if (nextIsString) {
try {
int idx = Integer.parseInt(lastContents);
lastContents = new XSSFRichTextString(sst.getEntryAt(idx))
.toString();
nextIsString = false;
} catch (Exception e) {
e.printStackTrace();
}
}
// v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
// 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
if (name.equals("v")) {
String value = lastContents.trim();
rowlist.add(curCol, value);
curCol++;
} else {
// 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
if (name.equals("row")) {
optRow(sheetIndex, curRow, rowlist);
rowlist.clear();
curRow++;
curCol = 0;
}
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
// 得到单元格内容的值
lastContents += new String(ch, start, length);
}
}
maven pom如下
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>3.9</version>
</dependency>
<dependency>
<groupId>xerces</groupId>
<artifactId>xerces</artifactId>
<version>2.4.0</version>
</dependency>
因为工具类中引用gson,slf4j可以直接删除(只是用来输出日志显示用的)