2017-01-12 2 views
0

Ситуация следующая: У меня есть простая программа, которая использует библиотеку Apache Poi для добавления одной строки данных в конец существующего xlsx-файла. См. НижеДобавление строки в большой файл xlsx (вне памяти)

File file = new File(input); 
XSSFWorkbook workbook = new XSSFWorkbook(file); 
XSSFSheet sheet = workbook.getSheetAt(0); 
XSSFRow row = sheet.createRow(sheet.getLastRowNum() + 1); 

После этого я буду перебирать строку и устанавливать CellValues. Но проблема в том, что на второй строке кода, как показано выше, я получаю ошибку из памяти. Есть ли способ добавить строку данных в существующий файл xlsx без необходимости полностью читать файл?

+0

Chek thi one: ** http: // stackoverflow.com/a/18415433/1652222 ** – ManishChristian

+0

Спасибо за ввод, но в этом случае они также создают Рабочую книгу. Это также приведет к ошибке в области кучи – PkertNL

+0

См. Http://stackoverflow.com/questions/35805878/add-content-to-a-very-large-excel-file-using-apache-poi-run-out-of -alternatives/35826943 # 35826943 –

ответ

0

(недостаточно репутации для добавления комментария) Вы пытались использовать SXSSFWorkbook вместо XSSFWorkbook?

+0

SXSSFWorkbook может писать только (более эффективный, чем XSSF), но мне все же нужно прочитать существующие данные из текущего файла, чтобы скопировать эти данные в SXFFWorkbook. Полагаю, пожалуйста, поправьте меня, если я ошибаюсь – PkertNL

+0

Вы правы, он может писать. – jorgegm

1

Если получение XSSFWorkbook терпит неудачу из-за из-за нехватки памяти ошибки и потребность читать и написать книгу, то ни SXSSF, ни SAX анализатор поможет. Он предназначен только для написания. Другой - только для чтения.

Оба подхода в соответствии нуждаются в знаниях о *.xlsx формате файла Office Open XML. В общем случае файл *.xlsx представляет собой архив ZIP, содержащий XML файлы и другие файлы в специальной структуре каталогов. Таким образом, можно распаковать файл *.xlsx с помощью программного обеспечения ZIP, чтобы посмотреть файлы XML. Формат файла был сначала стандартизован Ecma. Поэтому для дальнейших ресерхов я предпочитаю Ecma Markup Language Reference. Например, Row.

ReadAndWriteTest.xlsx, используемый в обоих примерах, должен иметь как минимум один рабочий лист, а первый рабочий лист должен иметь по крайней мере одну строку.

В одном подходе можно использовать методы DOMXMLBeans. Моя любимая ссылка для этого: grepcode.

Пример:

import org.apache.poi.openxml4j.opc.OPCPackage; 
import org.apache.poi.openxml4j.opc.PackagePart; 

import org.apache.poi.xssf.model.SharedStringsTable; 

import java.io.File; 
import java.io.OutputStream; 

import org.openxmlformats.schemas.spreadsheetml.x2006.main.WorksheetDocument; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTWorksheet; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTSheetData; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTCell; 
import org.openxmlformats.schemas.spreadsheetml.x2006.main.STCellType; 

import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; 

import org.apache.xmlbeans.XmlOptions; 

import javax.xml.namespace.QName; 

import java.util.Map; 
import java.util.HashMap; 

import java.util.regex.Pattern; 

class DOMReadAndWriteTest { 

public static void main(String[] args) { 
    try { 

    File file = new File("ReadAndWriteTest.xlsx"); 
    //we only open the OPCPackage, we don't create a Workbook 
    OPCPackage opcpackage = OPCPackage.open(file); 

    //if there are strings in the SheetData, we need the SharedStringsTable 
    PackagePart sharedstringstablepart = opcpackage.getPartsByName(Pattern.compile("/xl/sharedStrings.xml")).get(0); 
    SharedStringsTable sharedstringstable = new SharedStringsTable(); 
    sharedstringstable.readFrom(sharedstringstablepart.getInputStream()); 

    //get the PackagePart of the first sheet 
    PackagePart sheetpart = opcpackage.getPartsByName(Pattern.compile("/xl/worksheets/sheet1.xml")).get(0); 
    //get the worksheet from the first sheet's XML 
    //if it even fails while parsing this, then this approach is not usable 
    WorksheetDocument worksheetdocument = WorksheetDocument.Factory.parse(sheetpart.getInputStream()); 
    CTWorksheet worksheet = worksheetdocument.getWorksheet(); 

    CTSheetData sheetdata = worksheet.getSheetData(); 

    //put some data in 10 new rows" 
    for (int i = 0; i < 10; i++) { 
    int rowsCount = sheetdata.sizeOfRowArray(); 

    CTCell ctcell= sheetdata.addNewRow().addNewC(); 

    CTRst ctstr = CTRst.Factory.newInstance(); 
    ctstr.setT("new Row " + (rowsCount + 1)); 
    int sRef = sharedstringstable.addEntry(ctstr); 
    ctcell.setT(STCellType.S); 
    ctcell.setV(Integer.toString(sRef)); 

    ctcell=sheetdata.getRowArray(rowsCount).addNewC(); 
    ctcell.setV(""+rowsCount+"."+(i+1)+""+((i+2>9)?0:i+2)); 
    } 

    //write the SharedStringsTable 
    OutputStream out = sharedstringstablepart.getOutputStream(); 
    sharedstringstable.writeTo(out); 
    out.close(); 

    //create XmlOptions for saving the worksheet 
    XmlOptions xmlOptions = new XmlOptions(); 
    xmlOptions.setSaveOuter(); 
    xmlOptions.setUseDefaultNamespace(); 
    xmlOptions.setSaveAggressiveNamespaces(); 
    xmlOptions.setCharacterEncoding("UTF-8"); 
    xmlOptions.setSaveSyntheticDocumentElement(new QName(CTWorksheet.type.getName().getNamespaceURI(), "worksheet")); 
    Map<String, String> map = new HashMap<String, String>(); 
    map.put(STRelationshipId.type.getName().getNamespaceURI(), "r"); 
    xmlOptions.setSaveSuggestedPrefixes(map); 

    //save the worksheet 
    out = sheetpart.getOutputStream(); 
    worksheet.save(out, xmlOptions); 
    out.close(); 

    opcpackage.close(); 

    } catch (Exception ex) { 
    ex.printStackTrace(); 
    } 
} 
} 

Этот код записывает 10 новых строк в sheet1 из ReadAndWriteTest.xlsx без открытия всей книги. Но он должен по крайней мере открыть и разобрать лист1 и SharedStringsTable. Если даже это не удается, тогда этот подход не применим.

Другой подход может заключаться в использовании StAX. Этот API может читать и записывать XML-события. И он использует потоковое вещание.

Пример:

import org.apache.poi.openxml4j.opc.OPCPackage; 
import org.apache.poi.openxml4j.opc.PackagePart; 

import org.apache.poi.xssf.model.SharedStringsTable; 

import org.openxmlformats.schemas.spreadsheetml.x2006.main.CTRst; 

import javax.xml.stream.XMLEventFactory; 
import javax.xml.stream.XMLEventReader; 
import javax.xml.stream.XMLEventWriter; 
import javax.xml.stream.XMLInputFactory; 
import javax.xml.stream.XMLOutputFactory; 
import javax.xml.stream.events.Characters; 
import javax.xml.stream.events.StartElement; 
import javax.xml.stream.events.EndElement; 
import javax.xml.stream.events.Attribute; 
import javax.xml.stream.events.XMLEvent; 

import javax.xml.namespace.QName; 

import java.io.File; 
import java.io.InputStream; 
import java.io.OutputStream; 

import java.util.Arrays; 
import java.util.List; 
import java.util.regex.Pattern; 

class StaxReadAndWriteTest { 

public static void main(String[] args) { 
    try { 

    File file = new File("ReadAndWriteTest.xlsx"); 
    OPCPackage opcpackage = OPCPackage.open(file); 

    //if there are strings in the sheet data, we need the SharedStringsTable 
    //if it even fails while parsing this SharedStringsTable, then this approach is not usable 
    //then we must stream this XML event driven also. 
    PackagePart sharedstringstablepart = opcpackage.getPartsByName(Pattern.compile("/xl/sharedStrings.xml")).get(0); 
    SharedStringsTable sharedstringstable = new SharedStringsTable(); 
    sharedstringstable.readFrom(sharedstringstablepart.getInputStream()); 

    PackagePart sheetpart = opcpackage.getPartsByName(Pattern.compile("/xl/worksheets/sheet1.xml")).get(0); 

    XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(sheetpart.getInputStream()); 
    XMLEventWriter writer = XMLOutputFactory.newInstance().createXMLEventWriter(sheetpart.getOutputStream()); 

    XMLEventFactory eventFactory = XMLEventFactory.newInstance(); 

    int rowsCount = 0; 

    while(reader.hasNext()){ //loop over all XML in sheet1.xml 
    XMLEvent event = (XMLEvent)reader.next(); 
    writer.add(event); //by default write each readed event 

    if(event.isStartElement()){ 
    StartElement startElement = (StartElement)event; 
    QName startElementName = startElement.getName(); 
    if(startElementName.getLocalPart().equalsIgnoreCase("row")) { //start element of row 
     boolean rowStart = true; 
     rowsCount++; 
     do { 
     event = (XMLEvent)reader.next(); //find this row's end 
     writer.add(event); //by default write each readed event 

     if(event.isEndElement()){ 
     EndElement endElement = (EndElement)event; 
     QName endElementName = endElement.getName(); 
     if(endElementName.getLocalPart().equalsIgnoreCase("row")) { //end element of row 
     rowStart = false; 
     //we assume that there is nothing else (character data) between end element of row and next element 
     XMLEvent nextElement = (XMLEvent)reader.peek(); 
     QName nextElementName = null; 
     if (nextElement.isStartElement()) nextElementName = ((StartElement)nextElement).getName(); 
     else if (nextElement.isEndElement()) nextElementName = ((EndElement)nextElement).getName(); 
     if(!nextElementName.getLocalPart().equalsIgnoreCase("row")) { //next is not start element of row 
      //we have the last row, so we write new rows now 

      for (int i = 0; i < 10; i++) { 

      StartElement newRowStart = eventFactory.createStartElement(new QName("row"), null, null); 
      writer.add(newRowStart); 

//start cell A 
      Attribute attribute = eventFactory.createAttribute("t", "s"); 
      List attributeList = Arrays.asList(attribute); 
      StartElement newCellStart = eventFactory.createStartElement(new QName("c"), attributeList.iterator(), null); 
      writer.add(newCellStart); 

      CTRst ctstr = CTRst.Factory.newInstance(); 
      ctstr.setT("new Row " + (rowsCount +1)); 
      int sRef = sharedstringstable.addEntry(ctstr); 

      StartElement newCellValue = eventFactory.createStartElement(new QName("v"), null, null); 
      writer.add(newCellValue); 

      Characters value = eventFactory.createCharacters(Integer.toString(sRef)); 
      writer.add(value);   

      EndElement newCellValueEnd = eventFactory.createEndElement(new QName("v"), null); 
      writer.add(newCellValueEnd); 

      EndElement newCellEnd = eventFactory.createEndElement(new QName("c"), null); 
      writer.add(newCellEnd); 
//end cell A 
//start cell B 
      newCellStart = eventFactory.createStartElement(new QName("c"), null, null); 
      writer.add(newCellStart); 

      newCellValue = eventFactory.createStartElement(new QName("v"), null, null); 
      writer.add(newCellValue); 

      value = eventFactory.createCharacters(""+rowsCount+"."+(i+1)+""+((i+2>9)?0:i+2)); 
      writer.add(value);   

      newCellValueEnd = eventFactory.createEndElement(new QName("v"), null); 
      writer.add(newCellValueEnd); 

      newCellEnd = eventFactory.createEndElement(new QName("c"), null); 
      writer.add(newCellEnd); 
//end cell B 

      EndElement newRowEnd = eventFactory.createEndElement(new QName("row"), null); 
      writer.add(newRowEnd); 

      rowsCount++; 
      } 
     } 
     } 
     } 
     } while (rowStart); 
    } 
    } 
    } 

    writer.flush(); 

    //write the SharedStringsTable 
    OutputStream out = sharedstringstablepart.getOutputStream(); 
    sharedstringstable.writeTo(out); 
    out.close(); 

    opcpackage.close(); 

    } catch (Exception ex) { 
    ex.printStackTrace(); 
    } 
} 
} 

Этот код также записывает 10 новых строк в sheet1 из ReadAndWriteTest.xlsx без открытия всей книги. Но он должен по крайней мере открыть и разобрать SharedStringsTable. Если даже это не удается, то этот подход также неприменим. Но, конечно, даже SharedStringsTable можно было передавать с помощью StAX. Но, как вы видите в примере с генерацией строк и ячеек, это намного сложнее. Поэтому использование SharedStringsTable упрощает работу в этом примере.

+0

Спасибо за ваш вклад! К сожалению файл большой, чтобы открыть (и разобрать) оба подхода. – PkertNL

Смежные вопросы