Apache POI – FileInputStream起作用,File对象失败(NullPointerException)

我尝试将所有工作表从一个工作簿复制到另一个工作簿。 事情是,如果我通过FileInputStreams读取工作簿,它正常工作,但它不适用于文件对象。

考虑以下方法:

import java.io.BufferedReader; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import org.apache.commons.io.IOUtils; import org.apache.commons.io.filefilter.WildcardFileFilter; import org.apache.poi.EncryptedDocumentException; import org.apache.poi.openxml4j.exceptions.InvalidFormatException; import org.apache.poi.ss.SpreadsheetVersion; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.CellType; import org.apache.poi.ss.usermodel.DataConsolidateFunction; import org.apache.poi.ss.usermodel.DateUtil; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; import org.apache.poi.ss.util.AreaReference; import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.usermodel.XSSFPivotTable; import org.apache.poi.xssf.usermodel.XSSFSheet; public void copyAllSheetsAcrossWorkbook(String oldWorkbook, String newWorkbook) throws EncryptedDocumentException, InvalidFormatException, IOException { FileInputStream fisOld = null; FileInputStream fisNew = null; Workbook oldWB = null; Workbook newWB = null; FileOutputStream fileOut = null; System.out.println("oldWorkbook: " + oldWorkbook); System.out.println("newWorkbook: " + newWorkbook); fisOld = new FileInputStream(oldWorkbook); fisNew = new FileInputStream(newWorkbook); // THIS WORKS // oldWB = WorkbookFactory.create(fisOld); // newWB = WorkbookFactory.create(fisNew); // THIS DOES NOT WORK oldWB = WorkbookFactory.create(new File(oldWorkbook)); newWB = WorkbookFactory.create(new File(newWorkbook)); if (newWB == null) { System.out.println("newWB is null"); } // CellStyle newStyle = newWB.createCellStyle(); Row row; Cell cell; copiedSheets = new ArrayList<String>(); for (int i = 0; i < oldWB.getNumberOfSheets(); i++) { XSSFSheet sheetFromOldWB = (XSSFSheet) oldWB.getSheetAt(i); String sheetNameFromOldWB = sheetFromOldWB.getSheetName(); XSSFSheet sheetForNewWB = (XSSFSheet) newWB.getSheet(sheetNameFromOldWB); if (sheetForNewWB != null) { int sheetIndex = newWB.getSheetIndex(sheetNameFromOldWB); newWB.removeSheetAt(sheetIndex); } LOGGER.info("Copying to new Workbook: " + sheetNameFromOldWB); sheetForNewWB = (XSSFSheet) newWB.createSheet(sheetFromOldWB.getSheetName()); for (int rowIndex = 0; rowIndex < sheetFromOldWB.getPhysicalNumberOfRows(); rowIndex++) { row = sheetForNewWB.createRow(rowIndex); for (int colIndex = 0; colIndex < sheetFromOldWB.getRow(rowIndex).getPhysicalNumberOfCells(); colIndex++) { cell = row.createCell(colIndex); // get cell from old WB's sheet and when cell is null, return as blank cells. Cell c = sheetFromOldWB.getRow(rowIndex).getCell(colIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK); // Below is where all the copying is happening. // CellStyle origStyle = c.getCellStyle(); // newStyle.cloneStyleFrom(origStyle); // cell.setCellStyle(newStyle); switch (c.getCellTypeEnum()) { case STRING: cell.setCellValue(c.getRichStringCellValue().getString()); break; case NUMERIC: if (DateUtil.isCellDateFormatted(cell)) { cell.setCellValue(c.getDateCellValue()); } else { cell.setCellValue(c.getNumericCellValue()); } break; case BOOLEAN: cell.setCellValue(c.getBooleanCellValue()); break; case FORMULA: cell.setCellFormula(c.getCellFormula()); break; default: break; } } } copiedSheets.add(oldWB.getSheetName(i)); } fileOut = new FileOutputStream(newWorkbook); newWB.write(fileOut); // <------ HERE I GET NULLPOINTEREXCEPTION fisOld.close(); fisNew.close(); oldWB.close(); fileOut.close(); newWB.close(); 

我在newWB.write(fileOut);得到以下exception: :

 Exception in thread "main" org.apache.poi.POIXMLException: java.lang.NullPointerException at org.apache.poi.POIXMLDocument.getProperties(POIXMLDocument.java:168) at org.apache.poi.POIXMLDocument.write(POIXMLDocument.java:246) at com.capgemini.toolkit.App.copyAllSheetsAcrossWorkbook(App.java:263) at com.capgemini.toolkit.App.main(App.java:58) Caused by: java.lang.NullPointerException at org.apache.poi.openxml4j.util.ZipSecureFile$ThresholdInputStream.read(ZipSecureFile.java:210) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager$RewindableInputStream.read(Unknown Source) at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.setupCurrentEntity(Unknown Source) at com.sun.org.apache.xerces.internal.impl.XMLVersionDetector.determineDocVersion(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(Unknown Source) at com.sun.org.apache.xerces.internal.parsers.DOMParser.parse(Unknown Source) at com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderImpl.parse(Unknown Source) at javax.xml.parsers.DocumentBuilder.parse(Unknown Source) at org.apache.poi.util.DocumentHelper.readDocument(DocumentHelper.java:140) at org.apache.poi.POIXMLTypeLoader.parse(POIXMLTypeLoader.java:143) at org.openxmlformats.schemas.officeDocument.x2006.extendedProperties.PropertiesDocument$Factory.parse(Unknown Source) at org.apache.poi.POIXMLProperties.<init>(POIXMLProperties.java:78) at org.apache.poi.POIXMLDocument.getProperties(POIXMLDocument.java:166) ... 3 more 

在POI文档中,由于内存消耗较低,总是提到更好地使用File对象。 这就是为什么我想知道为什么它不适用于一个File对象。

为了testing,这是在主要方法中运行的唯一方法,我使用了2个新的Excel文件(.xlsx)和一些虚拟数据。

有没有人看到为什么它不适用于一个File对象? 难道我做错了什么?

供参考:我使用兴趣点3.16。

使用File而不是FileInputStream打开Workbook导致内存占用更less,因为如果使用XSSF*.xlsx ), ZipPackage将直接从*.xlsx文件打开,而不是将整个ZIP内容读入记忆。

但是这也意味着ZipPackage会在打开文档之前打开文件。 因此,直到Workbook将被closures,没有什么可以同时写入该文件。 因此,由于没有可能将Workbook内容写回到打开Workbook的同一文件中,因此如果您只想从该Workbook读取,则可以使用File而不是FileInputStream来打开Workbook 。 但是,如果你想读取和写入同一个文件,这是行不通的。 然后FileInputStreamFileOutputStream是必需的。

所以在你的情况下,你会尝试从File读取Workbook newWB ,然后使用Workbook写入同一个文件

 fileOut = new FileOutputStream(newWorkbook); newWB.write(fileOut); 

而文件已经打开。 这失败了。

但:

  fisNew = new FileInputStream(newWorkbook); oldWB = WorkbookFactory.create(new File(oldWorkbook)); newWB = WorkbookFactory.create(fisNew); ... fileOut = new FileOutputStream(newWorkbook); newWB.write(fileOut); oldWB.close(); newWB.close(); 

应该pipe用。

顺便说一句:如果你正在使用一个File ,那么你不应该为同一个文件使用FileInputStream 。 所以不要使用fisOld

使用File而不是FileInputStream打开Workbook另一个缺点是,在closuresWorkbook并隐式closures下层文件系统时(如果是POIFSFileSystem ,则使用HSSFZipPackage时为XSSF ),则文件将获得更新的上次修改date。 文件中没有改动,但文件已经打开并且新写入文件系统。 这就是为什么上次修改date已更新。


编辑2017年9月21日:使用File的缺点似乎比首先想到的更大。 OPCPackage.close还将所有更改保存到底层的OPCPackage 。 因此,如果您从文件中打开XSSFWorkbook ,然后想使用write(java.io.OutputStream stream)将changings写入另一个文件,那么在closuresOPCPackage时,源文件也将被更改。 只有在从XSSFWorkbook使用write(java.io.OutputStream stream)时才会出现这个问题,因为调用了POIXMLDocument.write ,调用了POIXMLDocumentPart.onSave ,它保存了底层OOXML包中的变更。 所以OPCPackage在closures之前会更新所有更改。

简短例子:

 import org.apache.poi.ss.usermodel.*; import java.io.File; import java.io.FileOutputStream; class ReadAndWriteExcelWorkbook { public static void main(String[] args) throws Exception { Workbook workbook = WorkbookFactory.create(new File("file.xlsx")); Sheet sheet = workbook.getSheetAt(0); Row row = sheet.getRow(0); if (row == null) row = sheet.createRow(0); Cell cell = row.getCell(0); if (cell == null) cell = row.createCell(0); cell.setCellValue("changed"); workbook.write(new FileOutputStream("fileNew.xlsx")); workbook.close(); } } 

在此代码之后, fileNew.xlsx以及file.xlsx都被更改。