在读取excel大文件的时候就不能再使用poi包下面的Workbook类,会造成OOM等问题。
我们常见的excel分为xlsx格式和csv格式。分别实现一下。
xlsx格式处理。
- 需要的pom依赖:
<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId>poi</artifactId>
<version>4.1.2</version>
</dependency>
<!-- /artifact//poi-ooxml -->
<dependency>
<groupId></groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
java代码如下
import org.apache.poi.openxml4j.opc.OPCPackage;
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;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @program: automan-admin
* @description: 该类的主要是作用逐行解析xlsx文件,适合大文件操作
* @author: xpb
* @date: 2020-12-17 14:14
**/
public abstract class ExcelUtil<E> 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;
//该字段用于你解析后需要的关联处理,不需要则传null
private E object;
/**
* 读取第一个工作簿的入口方法
* @param path
*/
public void readOneSheet(String path, E e) throws Exception {
this.object = e;
//该处读取的是远程存储的url,如果是本地文件,可以使用下面的语句
OPCPackage pkg = OPCPackage.open(new URL(path).openStream());
//OPCPackage pkg = (path);
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 path
* @throws Exception
*/
public void process(String path) throws Exception {
OPCPackage pkg = OPCPackage.open(path);
XSSFReader r = new XSSFReader(pkg);
SharedStringsTable sst = r.getSharedStringsTable();
XMLReader parser = fetchSheetParser(sst);
Iterator<InputStream> sheets = r.getSheetsData();
while (sheets.hasNext()) {
curRow = 0;
sheetIndex++;
InputStream sheet = sheets.next();
InputSource sheetSource = new InputSource(sheet);
parser.parse(sheetSource);
sheet.close();
}
}
/**
* 该方法自动被调用,每读一行调用一次,在方法中写自己的业务逻辑即可
* 使用的时候重写该方法。
* @param sheetIndex 工作簿序号
* @param curRow 处理到第几行
* @param rowList 当前数据行的数据集合
*/
public abstract void optRow(int sheetIndex, int curRow, List<String> rowList, E e);
public XMLReader fetchSheetParser(SharedStringsTable sst) throws SAXException, SAXException {
XMLReader parser = XMLReaderFactory
.createXMLReader("");
this.sst = sst;
parser.setContentHandler(this);
return parser;
}
public void startElement(String uri, String localName, String name,
Attributes attributes){
// c => 单元格
if (name.equals("c")) {
// 如果下一个元素是 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) {
// 根据SST的索引值的到单元格的真正要存储的字符串
// 这时characters()方法可能会被调用多次
if (nextIsString) {
try {
int idx = Integer.parseInt(lastContents);
lastContents = new XSSFRichTextString(sst.getEntryAt(idx))
.toString();
} catch (Exception e) {
}
}
// v => 单元格的值,如果单元格是字符串则v标签的值为该字符串在SST中的索引
// 将单元格内容加入rowlist中,在这之前先去掉字符串前后的空白符
if (name.equals("v")) {
String value = lastContents.trim();
value = value.equals("") ? " " : value;
rowlist.add(curCol, value);
curCol++;
} else {
// 如果标签名称为 row ,这说明已到行尾,调用 optRows() 方法
if (name.equals("row")) {
optRow(sheetIndex, curRow, rowlist, object);
rowlist.clear();
curRow++;
curCol = 0;
}
}
}
public void characters(char[] ch, int start, int length)
throws SAXException {
// 得到单元格内容的值
lastContents += new String(ch, start, length);
}
}
如何使用上面的工具类
/**
* @program: mock-demo
* @description: .
* @author: xpb
* @date: 2020-12-21 20:41
**/
public class TestExcelUtil extends ExcelUtil<String>{
@Override
public void optRow(int sheetIndex, int curRow, List<String> rowList, String s) {
//逐行处理读取的数据
}
}
使用:
/**
* @program: mock-demo
* @description: ..
* @author: xupengbo
* @date: 2020-12-21 20:43
**/
public class HandleTest {
public static void main(String[] args) throws Exception {
TestExcelUtil testExcelUtil = new TestExcelUtil();
testExcelUtil.readOneSheet("", "aaaa");
}
}
csv文件处理
public static void main(String[] args) throws Exception {
//这是读取远程文件
BufferedReader reader = new BufferedReader(new InputStreamReader(new URL("远程连接").openStream()));
//();//第一行信息,为标题信息,不用,如果需要,注释掉
String line = null;
while ((line = reader.readLine()) != null) {
String item[] = line.split(",");//CSV格式文件为逗号分隔符文件,这里根据逗号切分
//如果单元格内存在逗号,需要自行处理一下。
}
}