【问题标题】:Modifying existing xlsx spreadsheet using Apache POI leads to unreadable content error使用 Apache POI 修改现有 xlsx 电子表格会导致内容不可读错误
【发布时间】:2016-01-22 19:22:28
【问题描述】:

因此,使用 Apache POI 在 Excel 中出现不可读内容错误的话题似乎很常见。但是,我很惊讶我仍然找不到我的问题的例子,特别是因为我正在尝试做的事情似乎非常简单。这让我相信,由于我对一些 java 对象的理解,特别是 File 和 FileInputStream,有些事情可能只是让我头疼。我通过大量的试验和错误发现了如何使它工作,但我的问题是为什么一个工作,为什么另一个不工作,并且很想了解潜在的问题。我认为它也可以帮助其他人理解。这正是我想要做的:

打开现有的 xlsx 文件,向其中添加工作表,然后将其保存为与其原始文件名相同的文件名。本质上,只需修改现有的 xlsx 文件即可添加工作表。

下面是不起作用的代码和起作用的代码,我想知道为什么使用 File 对象不起作用。在这两个示例中,我已经创建了 TravelVouchers.xlsx 带有名为 Voucher_1 的工作表的文件,并且可以正常打开它。

以下代码导致travelVouchersWkBk.write(fileOut); 行上的 NullPointerException:

File travelVouchersFile = new File("./Output/TravelVouchers.xlsx");
Workbook travelVouchersWkBk = WorkbookFactory.create(travelVouchersFile);
travelVouchersWkBk.createSheet("Voucher_2");
FileOutputStream fileOut = new FileOutputStream(travelVouchersFile);
travelVouchersWkBk.write(fileOut);
fileOut.flush();
fileOut.close();

虽然以下代码效果很好:

File travelVouchersFile = new File("./Output/TravelVouchers.xlsx");
FileInputStream fileIn = new FileInputStream(travelVouchersFile);
Workbook travelVouchersWkBk = WorkbookFactory.create(fileIn);
travelVouchersWkBk.createSheet("Voucher_2");
fileIn.close();
FileOutputStream fileOut = new FileOutputStream(travelVouchersFile);
travelVouchersWkBk.write(fileOut);
fileOut.flush();
fileOut.close();

在不起作用的代码中,导致excel中有不可读内容的错误,当我选择修复时,我可以打开它,它只有Voucher_1。因此,显然将 WorkbookFactory.create 与 File 对象一起使用是行不通的,而使用 FileInputStream 则可以,但我想知道我对 File 与 FileInputStream 的哪些方面不了解,因为它与此问题有关。

非常感谢您的澄清,我真的很感激! 保罗

【问题讨论】:

    标签: java excel apache-poi


    【解决方案1】:

    我相信使用FileWorkbookFactory 来读取Workbook 然后写入相同 Workbook-File 不是好主意,直​​到现在。 apache POI 文档提到:

    文件与输入流

    打开工作簿时,可以是 .xls HSSFWorkbook,也可以是 .xlsx XSSFWorkbook,工作簿可以从文件或 输入流。使用 File 对象可以降低内存消耗, 而 InputStream 需要更多内存,因为它必须缓冲 整个文件。

    但是为什么使用 File 对象可以降低内存消耗?这是因为 Workbookfactory 然后创建了一个 RandomAccessFile ,它不需要在内存中完全读取。要验证,请阅读WorkbookFactory.java -> public static Workbook create(File file, String password, boolean readOnly)NPOIFSFileSystem.java -> private NPOIFSFileSystem(FileChannel channel, File srcFile, boolean readOnly, boolean closeChannelOnError)FileBackedDataSource.java -> private static RandomAccessFile newSrcFile(File file, String mode) 的来源。

    但据我所知,无法访问此RandomAccessFile。所以我们不能写它,也不能在不关闭整个Workbook的情况下关闭它。因此,在上面的示例中,RandomAccessFile 将被打开以进行读写(rw 模式),而Workbook.write(FileOutputStream) 尝试写入文件。

    所以使用File 可能有利于只读或从一个文件读取并保存到另一个文件。但是直到现在使用File 来读写同一个文件并不是一个好主意。对于这个FileInputStreamFileOutputStream 更好。

    【讨论】:

    • 我们一直希望有人会自愿为 POI 添加就地写入支持,但目前还没有人这样做。 NPIFS 和 OPC 都支持,只需要 HSSF/XSSF/etc 层中的代码即可使用。
    • @Gagravarr:你是 apache POI 开发团队的一员吗?如果是这样,根据我的经验,您的希望正是此类基于志愿者的项目的问题。如果要参与,那么这个人必须深入思考整个项目的编程概念。所以“......只需要 HSSF/XSSF/etc 层中的代码来使用它”并不是对问题的正确描述;-)。我假设poi.apache.org/apidocs/org/apache/poi/… 将是基于 XML 文件的缺失部分的入口点。但是之后? BIFF 呢?
    • 如果您想提供帮助,请加入开发列表 - 基本上它需要重构 write(OutputStream) 方法,以便他们可以重新使用开放的 NPOIFS/OPC,而不是总是重新创建一个新的。然后进行大量单元测试!
    • 哇非常感谢您的详细解释,非常感谢! :)
    猜你喜欢
    • 1970-01-01
    • 2013-01-29
    • 2012-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-02-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多