本次使用POI处理xlsx文件,莫名的遇到了一个无法逾越的问题。

总共71个xlsx文件,单个文件最大达到50M以上,71个xls文件摆在那里就有3-4G的大小。

在起始处理的时候,发现原本适用于正常大小的POI处理xls程序竟然屡次的报错GC outofmemory 的内存移除的问题。

【当前状况】

①一个50M大小的xlsx文件,使用压缩文件打开,可以看到xml文件达到900M以上

②一个50M大小以上的xlsx文件,单个工作簿,行数平均在15W行---40W之间,列数在64列左右

③单方面的调整了JVM运行的  堆内存大小,调整到了4G并没有什么效果

④程序运行起来之后,XSSFWorkbook workbook1 = new XSSFWorkbook(fileInputStream); 跑起来,CPU瞬间99%满负荷,内存最高可以使用9.79G的占用率,仅这一个xlsx文件

【官方解决方法】

网上关于POI处理超大文件的资料不很多,粘贴,水贴更是多如牛毛。POI官网,有一个SXSSF的XSSF扩展API插件,话说是可以【生成/处理】【有歧义】大型电子表格。

但是,仅仅是支持生成而已。

如果用于操作大型xlsx文件,API中给出的构造方法,还是需要先构造XSSFWorkbook对象。

在上一步就会卡死导致内存溢出,根本走不到下步。

最后,还是找到了一篇有用的信息,关于官方的解决思路,就是将xlsx文件转化为CVS文件进行相应的处理,这样不用占用太大的内存空间,导致内存溢出程序崩溃。

一下是官方提供的XLS转化为CVS的代码,注解做了部分翻译。留下之后有时间再进行研究。

 package com.poi.dealXlsx;

 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;  

 /* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream; import javax.xml.parsers.ParserConfigurationException; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.SAXHelper;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler;
import org.apache.poi.xssf.extractor.XSSFEventBasedExcelExtractor;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFComment;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; /**
*一个基本的XLSX - > CSV处理器
 * POI样本程序XLS2CSVmra从包中
 * org.apache.poi.hssf.eventusermodel.examples。
 *与HSSF版本一样,这会试图找到错误
 *行和单元格,并为它们输出空条目。
 * <p />
 *使用SAX解析器读取数据表以保持
 *内存占用比较小,所以这应该是
 *能够阅读庞大的工作簿。样式表和
 *共享字符串表必须保存在内存中。该
 *标准POI样式表类使用,但是一个自定义
 *(只读)类用于共享字符串表
 *因为标准的POI SharedStringsTable增长很大
 *快速与唯一字符串的数量。
 * <p />
 *更高级的SAX事件解析实现
 *的XLSX文件,请参阅{@link XSSFEventBasedExcelExtractor}
 *和{@link XSSFSheetXMLHandler}。请注意,在很多情况下,
 *可以简单地使用那些与习惯
 * {@link SheetContentsHandler}并且不需要SAX代码
 * 你自己!
*/
public class XLSX2CSV {
/**
      *使用XSSF Event SAX助手进行大部分工作
      *解析Sheet XML,并输出内容
      *作为(基本)CSV。
     */
private class SheetToCSV implements SheetContentsHandler {
private boolean firstCellOfRow = false;
private int currentRow = -1;
private int currentCol = -1; /**
* 输出缺失的行
* @param number
*/
private void outputMissingRows(int number) {
for (int i = 0; i < number; i++) {
for (int j = 0; j < minColumns; j++) {
output.append(',');
}
output.append('\n');
}
} @Override
public void startRow(int rowNum) {
// If there were gaps, output the missing rows
outputMissingRows(rowNum - currentRow - 1);
// Prepare for this row
firstCellOfRow = true;
currentRow = rowNum;
currentCol = -1;
} @Override
public void endRow(int rowNum) {
// Ensure the minimum number of columns
for (int i = currentCol; i < minColumns; i++) {
output.append(',');
}
output.append('\n');
} @Override
public void cell(String cellReference, String formattedValue,XSSFComment comment) {
if (firstCellOfRow) {
firstCellOfRow = false;
} else {
output.append(',');
} // gracefully handle missing CellRef here in a similar way as XSSFCell does
if (cellReference == null) {
cellReference = new CellAddress(currentRow, currentCol).formatAsString();
} // Did we miss any cells?
int thisCol = (new CellReference(cellReference)).getCol();
int missedCols = thisCol - currentCol - 1;
for (int i = 0; i < missedCols; i++) {
output.append(',');
}
currentCol = thisCol; // Number or string?
try {
Double.parseDouble(formattedValue);
output.append(formattedValue);
} catch (NumberFormatException e) {
output.append('"');
output.append(formattedValue);
output.append('"');
}
} @Override
public void headerFooter(String text, boolean isHeader, String tagName) {
// Skip, no headers or footers in CSV
}
} /**
* 表示可以存储多个数据对象的容器。
*/
private final OPCPackage xlsxPackage; /**
* 以最左边开始读取的列数
*/
private final int minColumns; /**
* 写出数据流
*/
private final PrintStream output; /**
* 创建一个新的XLSX -> CSV转换器
*
* @param pkg The XLSX package to process
* @param output The PrintStream to output the CSV to
* @param minColumns 要输出的最小列数,或-1表示最小值
*/
public XLSX2CSV(OPCPackage pkg, PrintStream output, int minColumns) {
this.xlsxPackage = pkg;
this.output = output;
this.minColumns = minColumns;
} /**
* Parses and shows the content of one sheet
* using the specified styles and shared-strings tables.
*解析并显示一个工作簿的内容
       *使用指定的样式和共享字符串表。
* @param styles 工作簿中所有工作表共享的样式表。
* @param strings 这是处理共享字符串表的轻量级方式。 大多数文本单元格将引用这里的内容。请注意,如果字符串由不同格式的位组成,则每个SI条目都可以有多个T元素
* @param sheetInputStream
*/
public void processSheet(StylesTable styles,
ReadOnlySharedStringsTable strings,
SheetContentsHandler sheetHandler, InputStream sheetInputStream)
throws IOException, ParserConfigurationException, SAXException {
DataFormatter formatter = new DataFormatter();
InputSource sheetSource = new InputSource(sheetInputStream);
try {
XMLReader sheetParser = SAXHelper.newXMLReader();
ContentHandler handler = new XSSFSheetXMLHandler(styles, null,
strings, sheetHandler, formatter, false);
sheetParser.setContentHandler(handler);
sheetParser.parse(sheetSource);
} catch (ParserConfigurationException e) {
throw new RuntimeException("SAX parser appears to be broken - "
+ e.getMessage());
}
} /**
* Initiates the processing of the XLS workbook file to CSV.
* 启动将XLS工作簿文件处理为CSV。
*
* @throws IOException
* @throws OpenXML4JException
* @throws ParserConfigurationException
* @throws SAXException
*/
public void process()
throws IOException, OpenXML4JException, ParserConfigurationException, SAXException {
ReadOnlySharedStringsTable strings = new ReadOnlySharedStringsTable(this.xlsxPackage);
XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
StylesTable styles = xssfReader.getStylesTable();
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
int index = 0;
while (iter.hasNext()) {
InputStream stream = iter.next();
String sheetName = iter.getSheetName();
this.output.println();
this.output.println(sheetName + " [index=" + index + "]:");
processSheet(styles, strings, new SheetToCSV(), stream);
stream.close();
++index;
}
} public static void main(String[] args) throws Exception {
/* if (args.length < 1) {
System.err.println("Use:");
System.err.println(" XLSX2CSV <xlsx file> [min columns]");
return;
}*/ File xlsxFile = new File("D:/基因数据测试/S1.xlsx");
if (!xlsxFile.exists()) {
System.err.println("没找到文件: " + xlsxFile.getPath());
return;
} int minColumns = -1;
if (args.length >= 2)
minColumns = Integer.parseInt(args[1]); // The package open is instantaneous, as it should be.
OPCPackage p = OPCPackage.open(xlsxFile.getPath(), PackageAccess.READ);
XLSX2CSV xlsx2csv = new XLSX2CSV(p, System.out, minColumns);
xlsx2csv.process();
p.close();
}
}

----------------------------------------------------------------------------------【待续1】------------------------------------------------------------------------------------------

上回书说道,官方提供的XLS转化为CVS代码,其实就是使用org.xml.sax.XMLReader使用SAX解析器去解析了xls底层的xml文件而已。并没有真正的转化为CVS文件。

【XMLReader就是JDK自带的方法,查阅API可以查看相关实现类和相关工具类的方法和使用】

对于上段代码的研读,贴出来:

 package com.poi.dealXlsx;

 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream; import javax.xml.parsers.ParserConfigurationException; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.SAXHelper;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFComment;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; /**
* 一个基本的XLSX - > CSV处理器 POI样本程序XLS2CSVmra从包中
* 实际上是将xlsx文件从传统的POI解析 转变为将xlsx底层的xml文件交给SAX解析器去解析
* 以便处理超大xlsx文件,从而占用更小的内存和CPU资源
*/
public class XLSX2CSV { /**
* XSSFSheetXMLHandler处理器,用来处理xlsx文件底层xml文件的sheet部分
*/
private class SheetToCSV implements SheetContentsHandler {
private boolean firstCellOfRow = false;
private int currentRow = -1;
private int currentCol = -1; /**
* 输出缺失的行
* @param number
*/
private void outputMissingRows(int number) {
for (int i = 0; i < number; i++) {
for (int j = 0; j < minColumns; j++) {
output.append(',');
}
output.println("缺失一行");
}
} //重新开始一行
@Override
public void startRow(int rowNum) {
outputMissingRows(rowNum - currentRow - 1);
firstCellOfRow = true;
currentRow = rowNum;
currentCol = -1;
} @Override
public void endRow(int rowNum) {
//确保最小的列数
for (int i = currentCol; i < minColumns; i++) {
output.println(rowNum+"行结束");
}
output.println("下标:"+rowNum+"行结束");
} @Override
public void cell(String cellReference, String formattedValue,XSSFComment comment) {
// 以下处理了单元格为Null的情况
//通过【A1】等判断是是【第一行第1列】
int thisCol = (new CellReference(cellReference)).getCol();
int missedCols = thisCol - currentCol - 1;
for (int i = 0; i < missedCols; i++) {
output.println("单元格值为空");
}
currentCol = thisCol;
if (firstCellOfRow) {
firstCellOfRow = false;
output.println(currentRow+"行的第一个单元格");
} //cellReference代表单元格的横纵坐标
if (cellReference == null) {
cellReference = new CellAddress(currentRow, currentCol).formatAsString();
} output.println("行"+currentRow+"+列"+currentCol+">>值:"+formattedValue);
} @Override
public void headerFooter(String text, boolean isHeader, String tagName) {
// CSV文件中没有页眉页脚
}
} /**
* 表示可以存储多个数据对象的容器。
*/
private final OPCPackage xlsxPackage; /**
* 以最左边开始读取的列数
*/
private final int minColumns; /**
* 写出数据流
*/
private final PrintStream output; /**
* 创建一个新的XLSX -> CSV转换器
*
* @param pkg The XLSX package to process
* @param output The PrintStream to output the CSV to
* @param minColumns 要输出的最小列数,或-1表示最小值
*/
public XLSX2CSV(OPCPackage pkg, PrintStream output, int minColumns) {
this.xlsxPackage = pkg;
this.output = output;
this.minColumns = minColumns;
} /**
*解析并显示一个工作簿的内容
* @param styles 工作簿中所有工作表共享的样式表。
* @param readOnlyTableString 这是处理共享字符串表的轻量级方式。 大多数文本单元格将引用这里的内容。请注意,如果字符串由不同格式的位组成,则每个SI条目都可以有多个T元素
* @param sheetInputStream 以sheet为单位的输入流
*/
public void processSheet(StylesTable styles,ReadOnlySharedStringsTable readOnlyTableString,SheetContentsHandler sheetHandler, InputStream sheetInputStream)
throws IOException, ParserConfigurationException, SAXException { DataFormatter formatter = new DataFormatter();
InputSource sheetSource = new InputSource(sheetInputStream);
try {
//为了尽可能的节省内存和I/0消耗,XmlReader读取Xml需要通过Read()实例方法,不断读取Xml文档中的声明,节点开始,节点内容,节点结束,以及空白等等,直到文档结束,Read()方法返回false
XMLReader reader = SAXHelper.newXMLReader();
//XSSFSheetXMLHandler处理器,用来处理xlsx文件底层xml文件的sheet部分,用于生成行和单元格事件
ContentHandler handler = new XSSFSheetXMLHandler(styles, null,readOnlyTableString, sheetHandler, formatter, false);
//允许应用程序注册内容事件处理程序
reader.setContentHandler(handler);
//解析XML文件
reader.parse(sheetSource);
} catch (ParserConfigurationException e) {
throw new RuntimeException("SAX解析器坏了"+ e.getMessage());
}
} /**
* Initiates the processing of the XLS workbook file to CSV.
* 启动将XLS工作簿文件处理为CSV。
*
* @throws IOException
* @throws OpenXML4JException
* @throws ParserConfigurationException
* @throws SAXException
*/
public void process()throws IOException, OpenXML4JException, ParserConfigurationException, SAXException { //用于构建XSSFSheetXMLHandler处理器所需要实例化的参数
ReadOnlySharedStringsTable readOnlyTableString = new ReadOnlySharedStringsTable(this.xlsxPackage);
//XSSFReader获取xlsx文件xml下的各个部分,适用于低内存sax解析或类似。 它构成了对XSSF的EventUserModel支持的核心部分。
XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
//工作簿中所有工作表共享的样式表
StylesTable styles = xssfReader.getStylesTable();
//从org.apache.poi.xssf.eventusermodel.XSSFReader中获取到Sheet中的数据用来迭代,交给SAX去解析
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
int index = 0;
//此处迭代以 sheet为单位,一个工作簿进行一次解析
while (iter.hasNext()) {
InputStream inputStream = iter.next();
String sheetName = iter.getSheetName();
this.output.println("开始解析》》》》》》》》》》》》》》》》》》》》");
this.output.println("工作簿名称:"+sheetName + " [下标=" + index + "]");
processSheet(styles, readOnlyTableString, new SheetToCSV(), inputStream);
inputStream.close();
++index;
}
} public static void main(String[] args) throws Exception {
File xlsxFile = new File("D:/基因数据测试/S1.xlsx");
if (!xlsxFile.exists()) {
System.err.println("没找到文件: " + xlsxFile.getPath());
return;
} int minColumns = -1; //打开一个POI容器 参数1 文档路径 参数2 READ READ_WRITE WRITE三种模式
OPCPackage p = OPCPackage.open(xlsxFile.getPath(), PackageAccess.READ); //自己创建的xls->cvs的转化器
XLSX2CSV xlsx2csv = new XLSX2CSV(p, System.out, minColumns);
xlsx2csv.process();
//关闭容器
p.close();
}
}

实际测试的,就是下面的文件:

文件大小51M大小左右

对于上面这段官方提供的方法,我的处理思想是:

将xlsx中的数据读取出来之后,进行每1000行数据一次拆分的做法,将上述较大的xls文件拆分为一个一个的小文件,然后使用 http://www.cnblogs.com/sxdcgaq8080/p/7344787.html

提供的方法迭代去处理文件数据。

明天,将具体的处理代码写出来。

----------------------------------------------------------------------------------【待续2】------------------------------------------------------------------------------------------

处理了将近两天的时间,终于把xlsx超大文件的拆解弄出来了。

 package com.poi.dealXlsx;

 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;  

 import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List; import javax.xml.parsers.ParserConfigurationException; import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.openxml4j.opc.PackageAccess;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.DataFormatter;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellAddress;
import org.apache.poi.ss.util.CellReference;
import org.apache.poi.util.SAXHelper;
import org.apache.poi.xssf.eventusermodel.ReadOnlySharedStringsTable;
import org.apache.poi.xssf.eventusermodel.XSSFReader;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler;
import org.apache.poi.xssf.eventusermodel.XSSFSheetXMLHandler.SheetContentsHandler;
import org.apache.poi.xssf.model.StylesTable;
import org.apache.poi.xssf.usermodel.XSSFComment;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.xml.sax.ContentHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader; /**
* 一个基本的XLSX - > CSV处理器 POI样本程序XLS2CSVmra从包中
* 实际上是将xlsx文件从传统的POI解析 转变为将xlsx底层的xml文件交给SAX解析器去解析
* 以便处理超大xlsx文件,从而占用更小的内存和CPU资源
*/
public class XLSX2CSV { /**
* XSSFSheetXMLHandler处理器,用来处理xlsx文件底层xml文件的sheet部分
*/
private class SheetToCSV implements SheetContentsHandler {
private boolean firstCellOfRow = false;
private int currentRow = -1;
private int currentCol = -1;
private List<String> firstRowValue = new ArrayList<String>();
/**
* 输出缺失的行
* @param number
*/
private void outputMissingRows(int number) {
for (int i = 0; i < number; i++) {
for (int j = 0; j < minColumns; j++) {
output.append(',');
}
output.println("缺失一行");
}
} //重新开始一行
@Override
public void startRow(int rowNum) {
outputMissingRows(rowNum - currentRow - 1);
firstCellOfRow = true;
currentRow = rowNum;
currentCol = -1;
} @Override
public void endRow(int rowNum) {
//确保最小的列数
for (int i = currentCol; i < minColumns; i++) {
output.println(rowNum+"行结束");
}
output.println("下标:"+rowNum+"行结束");
} /**
* 逻辑处理的地方
*/
@Override
public void cell(String cellReference, String formattedValue,XSSFComment comment) { // 以下处理了单元格为Null的情况
//通过【A1】等判断是是【第一行第1列】
int thisCol = (new CellReference(cellReference)).getCol();
int missedCols = thisCol - currentCol - 1;
for (int i = 0; i < missedCols; i++) {
output.println("单元格值为空");
}
currentCol = thisCol;
if (firstCellOfRow) {
firstCellOfRow = false;
output.println(currentRow+"行的第一个单元格");
} //cellReference代表单元格的横纵坐标
if (cellReference == null) {
cellReference = new CellAddress(currentRow, currentCol).formatAsString();
}
//打印出行列中的值
// output.println("行"+currentRow+"+列"+currentCol+">>值:"+formattedValue);
//单独把第一行 也就是列名 全部存储起来,用于插入每一个拆解开的xls文件中
if(currentRow == 0){
//如果是第一行的第一列 说明重新读取了一个新的原始文件 需要重新存储列名
if(currentCol == 0){
firstRowValue.clear();
}
firstRowValue.add(formattedValue); }else{
File file = new File("d:/分解xls"); //如果currentRow=0的时候应该重新创建文件夹,代表有开始拆解新的xls文件
//否则代表正在拆解当前的xls文件,应该在当前的文件夹中进行
if(currentRow == 0){
file = createDir(true,file);
}else{
file = createDir(false,file);
}
//此处按照每1000行拆解为一个xlsx文件,修改1000,即按照自定义的行数拆解为一个xlsx文件
//如果行号模1000=0 需要重新创建一个文件 否则就在当前文件中写入数据
file = currentRow%1000 == 0 && currentCol == 0 ? getFile(file,true) : getFile(file,false); try {
FileInputStream fileInputStream = new FileInputStream(file);
XSSFWorkbook workbook = new XSSFWorkbook(fileInputStream);
Sheet sheet = workbook.getSheetAt(0);
int lastRowNum = sheet.getLastRowNum();//当前xlsx中最大行
Row row = null;
if(lastRowNum == 1 & currentCol ==1){
row = sheet.createRow(0);
for (int i = 0; i < firstRowValue.size(); i++) {
Cell cell = row.createCell(i);
cell.setCellValue(firstRowValue.get(i));
}
}
if(lastRowNum == 0){
row = currentCol != 0 ? sheet.getRow(1):sheet.createRow(1);
}else{
row = currentCol != 0 ? sheet.getRow(lastRowNum):sheet.createRow(lastRowNum+1);
}
//为本单元格 赋值
Cell cell = row.createCell(currentCol);
cell.setCellValue(formattedValue);
FileOutputStream fileOutputStream = new FileOutputStream(file);
workbook.write(fileOutputStream);
fileOutputStream.close();
workbook.close(); } catch (FileNotFoundException e) {
output.println("不能获取到xlsx文件");
} catch (IOException e) {
output.println("不是xlsx文件");
}
} } @Override
public void headerFooter(String text, boolean isHeader, String tagName) {
// CSV文件中没有页眉页脚
} /**
* 得到要操作的xlsx文件
* @param file 文件目录
* @param flag 是否需要重新创建一个新的xlsx
* @return
*/
private File getFile(File file,boolean flag){
File [] fileList = file.listFiles();
Arrays.sort(fileList,new OrderFile());
String newDirName = "1";
if(fileList.length != 0){
if(flag){
try {
newDirName = String.valueOf(Integer.parseInt(fileList[fileList.length-1].getName().substring(0, fileList[fileList.length-1].getName().indexOf(".")))+1);
} catch (Exception e) {
System.out.println("文件名获取失败");
}
}else{
return fileList[fileList.length-1];
} }
String filePath = file.getAbsolutePath()+"/"+newDirName+".xlsx";
XSSFWorkbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet(newDirName);
try {
FileOutputStream out = new FileOutputStream(filePath);
workbook.write(out);
System.out.println("创建xls文件"+newDirName+".xlsx成功");
} catch (FileNotFoundException e) {
System.out.println("创建xls失败");
} catch (IOException e) {
System.out.println("创建xls失败");
}
return new File(filePath);
} /**
* 得到当前要操作的目录
* @param sign 标志 是否需要新创建一个文件夹
* @param file 文件夹路径
* @return
*/
private File createDir(boolean sign,File file){
File [] fileList = file.listFiles();
Arrays.sort(fileList,new OrderFileS());
if(fileList.length == 0){
File thisFile = new File(file.getAbsolutePath()+"/1/");
boolean flag = thisFile.mkdirs();
System.out.println("创建文件夹1"+flag);
return thisFile;
}else{
if(sign){
try {
String newDirName = String.valueOf(Integer.parseInt(fileList[fileList.length-1].getName())+1);
File thisFile = new File(file.getAbsolutePath()+"/"+newDirName+"/");
boolean flag = thisFile.mkdirs();
System.out.println("创建文件夹"+newDirName+flag);
return thisFile;
} catch (Exception e) {
System.out.println("顺序最后一个文件夹名字获取失败,类型转化失败");
}
}
}
return fileList[fileList.length-1];
}
} /**
* 重写Comparator接口的compare方法,可以根据FileName实现自定义排序
* @author SXD
*
*/
class OrderFile implements Comparator<File>{ @Override
public int compare(File o1, File o2) {
int fileName1 = 0;
int fileName2 = 0;
try {
fileName1 = Integer.parseInt(o1.getName().substring(0, o1.getName().indexOf(".")));
fileName2 = Integer.parseInt(o2.getName().substring(0, o2.getName().indexOf(".")));
} catch (Exception e) {
System.out.println("比较方法中 获取文件名失败");
}
return fileName1>fileName2 ? 1 : fileName1 == fileName2 ? 0 : -1;
} }
/**
* 排序文件夹
* @author SXD
*
*/
class OrderFileS implements Comparator<File>{ @Override
public int compare(File o1, File o2) {
int fileName1 = 0;
int fileName2 = 0;
try {
fileName1 = Integer.parseInt(o1.getName());
fileName2 = Integer.parseInt(o2.getName());
} catch (Exception e) {
System.out.println("比较方法中 获取文件夹名失败");
}
return fileName1>fileName2 ? 1 : fileName1 == fileName2 ? 0 : -1;
} } /**
* 表示可以存储多个数据对象的容器。
*/
private final OPCPackage xlsxPackage; /**
* 以最左边开始读取的列数
*/
private final int minColumns; /**
* 写出数据流
*/
private final PrintStream output; /**
* 创建一个新的XLSX -> CSV转换器
*
* @param pkg The XLSX package to process
* @param output The PrintStream to output the CSV to
* @param minColumns 要输出的最小列数,或-1表示最小值
*/
public XLSX2CSV(OPCPackage pkg, PrintStream output, int minColumns) {
this.xlsxPackage = pkg;
this.output = output;
this.minColumns = minColumns;
} /**
*解析并显示一个工作簿的内容
* @param styles 工作簿中所有工作表共享的样式表。
* @param readOnlyTableString 这是处理共享字符串表的轻量级方式。 大多数文本单元格将引用这里的内容。请注意,如果字符串由不同格式的位组成,则每个SI条目都可以有多个T元素
* @param sheetInputStream 以sheet为单位的输入流
*/
public void processSheet(StylesTable styles,ReadOnlySharedStringsTable readOnlyTableString,SheetContentsHandler sheetHandler, InputStream sheetInputStream)
throws IOException, ParserConfigurationException, SAXException { DataFormatter formatter = new DataFormatter();
InputSource sheetSource = new InputSource(sheetInputStream);
try {
//为了尽可能的节省内存和I/0消耗,XmlReader读取Xml需要通过Read()实例方法,不断读取Xml文档中的声明,节点开始,节点内容,节点结束,以及空白等等,直到文档结束,Read()方法返回false
XMLReader reader = SAXHelper.newXMLReader();
//XSSFSheetXMLHandler处理器,用来处理xlsx文件底层xml文件的sheet部分,用于生成行和单元格事件
ContentHandler handler = new XSSFSheetXMLHandler(styles, null,readOnlyTableString, sheetHandler, formatter, false);
//允许应用程序注册内容事件处理程序
reader.setContentHandler(handler);
//解析XML文件
reader.parse(sheetSource);
} catch (ParserConfigurationException e) {
throw new RuntimeException("SAX解析器坏了"+ e.getMessage());
}
} /**
* Initiates the processing of the XLS workbook file to CSV.
* 启动将XLS工作簿文件处理为CSV。
*
* @throws IOException
* @throws OpenXML4JException
* @throws ParserConfigurationException
* @throws SAXException
*/
public void process()throws IOException, OpenXML4JException, ParserConfigurationException, SAXException { //用于构建XSSFSheetXMLHandler处理器所需要实例化的参数
ReadOnlySharedStringsTable readOnlyTableString = new ReadOnlySharedStringsTable(this.xlsxPackage);
//XSSFReader获取xlsx文件xml下的各个部分,适用于低内存sax解析或类似。 它构成了对XSSF的EventUserModel支持的核心部分。
XSSFReader xssfReader = new XSSFReader(this.xlsxPackage);
//工作簿中所有工作表共享的样式表
StylesTable styles = xssfReader.getStylesTable();
//从org.apache.poi.xssf.eventusermodel.XSSFReader中获取到Sheet中的数据用来迭代,交给SAX去解析
XSSFReader.SheetIterator iter = (XSSFReader.SheetIterator) xssfReader.getSheetsData();
int index = 0;
//此处迭代以 sheet为单位,一个工作簿进行一次解析
while (iter.hasNext()) {
InputStream inputStream = iter.next();
String sheetName = iter.getSheetName();
this.output.println("开始解析》》》》》》》》》》》》》》》》》》》》");
this.output.println("工作簿名称:"+sheetName + " [下标=" + index + "]");
processSheet(styles, readOnlyTableString, new SheetToCSV(), inputStream);
inputStream.close();
++index;
}
} //可以在这里循环处理多个超大xlsx文件,逻辑中已经处理 一个超大的xlsx文件拆解开的小的xlsx文件会放在一个文件夹中
public static void main(String[] args) throws Exception {
/**********这段代码,处理多个xlsx文件,只要循环去取即可************/
File xlsxFile = new File("D:/基因数据测试/S1.xlsx");
if (!xlsxFile.exists()) {
System.err.println("没找到文件: " + xlsxFile.getPath());
return;
}
/***********************/
int minColumns = -1; //打开一个POI容器 参数1 文档路径 参数2 READ READ_WRITE WRITE三种模式
OPCPackage p = OPCPackage.open(xlsxFile.getPath(), PackageAccess.READ); //自己创建的xls->cvs的转化器
XLSX2CSV xlsx2csv = new XLSX2CSV(p, System.out, minColumns);
xlsx2csv.process();
//关闭容器
p.close();
}
}

亲测 ,发现将50行拆解为一个xlsx文件,整体的速度是最合适的。 如果1000行拆解为一个xlsx文件,速度非常慢,因为每处理一个cell的数据,都会去获取xlsx文件对象。越到后面,xlsx文件中行数越大,数据越多,获取到这个xlsx对象也就越来越难以忍受。

----------------------------------------------------------------------------------待定,之后使用多线程和对象池来提高速度------------------------------------------------------------------------------------------------

【POI】对于POI无法处理超大xls等文件,官方解决方法【已解决】【多线程提升速率待定】的更多相关文章

  1. POI 导出excel带小数点的数字格式显示不对解决方法

    最近看到了一个问题就是java导出excel中带小数点的数字显示不对, 比如我想在excel中第一行显示:  3,000.0 但是在excle中导出的格式总是不带小数点 3000(非文本格式),而且也 ...

  2. POI解析excel,将批量数据写入文件或数据库

    .personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...

  3. poi导出excel出现本工作薄不能再使用其他新字体的解决方法

    最近使用POI处理EXCEL,当处理的单元格太多时,就会出现,本工作薄使用字体过多,不能再使用其他新的字体的是提示. 网上很多方法告诉我,要怎么修改excel文件,但是这个解决不了问题啊,难道让客户去 ...

  4. [转]SQL server2008 导入超大SQL脚本文件(超过10M)

    同事给我一个sqlserver的学习库,sql脚本导出有300m,gui执行有内存溢出的错误报出来,所以问了一下度娘,学而时习之:) 1. SQL server2008 导入超大SQL脚本文件(超过1 ...

  5. 使用phpExcelReader操作excel提示The filename *.xls is not readable的详细解决方法

    使用phpExcelReader操作excel提示The filename *.xls is not readable的详细解决方法 是xls文件有问题,另存为新的xls文件,然后导入就不会有这个问题

  6. 【Easyexcel】java导入导出超大数据量的xlsx文件 解决方法

    解决方法: 使用easyexcel解决超大数据量的导入导出xlsx文件 easyexcel最大支持行数 1048576. 官网地址: https://alibaba-easyexcel.github. ...

  7. xls/csv文件转换成dbf文件

    转至:https://blog.csdn.net/linhai1028/article/details/80211252 编写的一个小脚本,主要是利用python中的pandas,xlrd,dbfpy ...

  8. POI使用:解析xls/xlsx文件(兼容office2003/2007/2010版本)

    package cn.eguid; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; imp ...

  9. java使用poi.3.10读取excel 2003 (xls格式)

    最近在做一个Excel导入数据库的案例,整理文档出来供大家参考. 1.下载 最新的 poi http://poi.apache.org/download.html    2.解压 把相关jar包引进项 ...

随机推荐

  1. Liberty中应用的contextroot

    参考:http://www-01.ibm.com/support/knowledgecenter/api/content/SSEQTP_8.5.5/com.ibm.websphere.wlp.doc/ ...

  2. win 7 64 位系统驱动签名

    自己开发未经签名的驱动无法加载,关闭Windows 7系统中的驱动签名强制要求 bcdedit.exe -set loadoptions DDISABLE_INTEGRITY_CHECKS

  3. JHDU 2601 An easy problem (数学 )

    title: An easy problem 数学 杭电2601 tags: [数学] 题目链接 Problem Description When Teddy was a child , he was ...

  4. 【Python实例二】BeautifulSoup爬虫简单实践

    前言 前面安装了BeautifulSoup库,现在就来实现一下吧. 目录 一.Urllib库的使用 二.BeautifulSoup的使用 三. 一个示例 ----------------------- ...

  5. python3 uper(),继承实现原理,封装

    抽象类:本身不能被实例化,也不应该不实例化,它的作用就定义标准,并不用具体实现 import abc class Parent(metaclass=abc.ABCMeta): x=1 @abc.abs ...

  6. 转 appium解决每次运行都需要安装Unlock以及AppiumSetting的问题

    一.需要解决的问题 在部分android机型上每次运行最新版的appium-desktop都需要安装AppiumSetting以及Unlock,并且安装过程需要用户手动来确认,即使测试机上已经安装了这 ...

  7. python进程池pool的starmap的使用

    #!/usr/bin/env python3 from functools import partial from itertools import repeat from multiprocessi ...

  8. 热安装NGINX并支持多站点SSL

    https://www.moonfly.net/801.html http://www.centoscn.com/image-text/config/2015/0423/5251.html 1.查看n ...

  9. js实现双指缩放图片 手机端双指缩放图片

    首先引入js文件,需要jq,pinchzoom.js.pinchzoom.js需要在jq环境下使用,可以 <meta name="viewport" content=&quo ...

  10. JavaScript的数组详解

    #转载请留言联系 创建数组 1.通过new Array()进行创建 var arr1=new Array(); 2.通过中括号进行创建 var arr2=[]; 计算数组的长度 var arr3=[' ...