【POI】对于POI无法处理超大xls等文件,官方解决方法【已解决】【多线程提升速率待定】
本次使用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等文件,官方解决方法【已解决】【多线程提升速率待定】的更多相关文章
- POI 导出excel带小数点的数字格式显示不对解决方法
最近看到了一个问题就是java导出excel中带小数点的数字显示不对, 比如我想在excel中第一行显示: 3,000.0 但是在excle中导出的格式总是不带小数点 3000(非文本格式),而且也 ...
- POI解析excel,将批量数据写入文件或数据库
.personSunflowerP { background: rgba(51, 153, 0, 0.66); border-bottom: 1px solid rgba(0, 102, 0, 1); ...
- poi导出excel出现本工作薄不能再使用其他新字体的解决方法
最近使用POI处理EXCEL,当处理的单元格太多时,就会出现,本工作薄使用字体过多,不能再使用其他新的字体的是提示. 网上很多方法告诉我,要怎么修改excel文件,但是这个解决不了问题啊,难道让客户去 ...
- [转]SQL server2008 导入超大SQL脚本文件(超过10M)
同事给我一个sqlserver的学习库,sql脚本导出有300m,gui执行有内存溢出的错误报出来,所以问了一下度娘,学而时习之:) 1. SQL server2008 导入超大SQL脚本文件(超过1 ...
- 使用phpExcelReader操作excel提示The filename *.xls is not readable的详细解决方法
使用phpExcelReader操作excel提示The filename *.xls is not readable的详细解决方法 是xls文件有问题,另存为新的xls文件,然后导入就不会有这个问题
- 【Easyexcel】java导入导出超大数据量的xlsx文件 解决方法
解决方法: 使用easyexcel解决超大数据量的导入导出xlsx文件 easyexcel最大支持行数 1048576. 官网地址: https://alibaba-easyexcel.github. ...
- xls/csv文件转换成dbf文件
转至:https://blog.csdn.net/linhai1028/article/details/80211252 编写的一个小脚本,主要是利用python中的pandas,xlrd,dbfpy ...
- POI使用:解析xls/xlsx文件(兼容office2003/2007/2010版本)
package cn.eguid; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; imp ...
- java使用poi.3.10读取excel 2003 (xls格式)
最近在做一个Excel导入数据库的案例,整理文档出来供大家参考. 1.下载 最新的 poi http://poi.apache.org/download.html 2.解压 把相关jar包引进项 ...
随机推荐
- bzoj3223 文艺平衡树 codevs3303 翻转区间
splay模版题吧 只有区间翻转 至于为什么要把须翻转区间旋到根 因为查找一个区间可以先找出他左端点左边第一个点和右端点x右边第一个点y 然后将x旋到根节点 y旋到x的右儿子 这样x的右边的点就是所有 ...
- poj3580 序列之王 fhqtreap
fhqtreap的写法 操作其实都差不多哇 #include<cstdio> #include<cstring> #include<algorithm> using ...
- python学习笔记 IO 文件读写
读写文件是最常见的IO操作.python内置了读写文件的函数. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统完成的,现代操作系统不允许普通的程序直接对磁盘进行操作,所以, 读写 ...
- malloc和new的区别 end
3. c++中new的几种用法 c++中,new的用法很灵活,这里进行了简单的总结: 1. new() 分配这种类型的一个大小的内存空间,并以括号中的值来初始化这个变量; 2. new[] 分配这种类 ...
- String类的常见方法的使用案例
String类的常见方法的使用案例 //使用指定的字符串替换当前字符串中指定的内容 //将helloworld中的o替换为a String s="HelloWorld"; Stri ...
- SVN代码提交
SVN代码提交(转载) 原文链接:http://www.softown.cn/post/100.html 1.SVN代码提交 1) 原则 先更新再提交: SVN是为了多人协同开发而产生的,如果你在提交 ...
- centos7当中的systemd及systemctl(节选)
全面进入centos7时代,这个东东是需要系统了解的. http://blog.jobbole.com/85070/?utm_source=blog.jobbole.com&utm_mediu ...
- [BZOJ4553][Tjoi2016&Heoi2016]序列 cdp分治+dp
4553: [Tjoi2016&Heoi2016]序列 Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 260 Solved: 133[Sub ...
- AC日记——Collectors Problem uva 10779
UVA - 10779 思路: 最大流: s向所有的贴纸的种类连边,流量为Bob拥有的数量: 然后,Bob的朋友如果没有这种贴纸,则这种贴纸向bob的朋友连边,容量1: 如果bob的朋友的贴纸很多大于 ...
- 最小生成树的Prim算法
构造最小生成树的Prim算法 假设G=(V,E)为一连通网,其中V为网中所有顶点的集合,E为网中所有带权边的集合.设置两个新的集合U和T,其中集合U用于存放G的最小生成树的顶点,集合T用于 ...