Java 如何摆脱;保存更改?“;提示使用Apache POI XSSF创建的xlsx文件

Java 如何摆脱;保存更改?“;提示使用Apache POI XSSF创建的xlsx文件,java,xlsx,xssf,Java,Xlsx,Xssf,打开并立即关闭使用ApachePOIXSSF创建的xlsx文件后,系统会提示我保存未保存的更改。据我所知,这是因为我在xlsx文件中使用了公式 根据javadoc,这应该通过设置XSSFWorkbook.setForceFormulaRecalculation(true) 然而,这并不能解决问题 我还尝试在保存文件之前手动重新计算公式,但没有成功 SSCCE: import java.io.FileOutputStream; import java.io.IOException; import

打开并立即关闭使用ApachePOIXSSF创建的xlsx文件后,系统会提示我保存未保存的更改。据我所知,这是因为我在xlsx文件中使用了公式

根据javadoc,这应该通过设置
XSSFWorkbook.setForceFormulaRecalculation(true)
然而,这并不能解决问题

我还尝试在保存文件之前手动重新计算公式,但没有成功

SSCCE:

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFFormulaEvaluator;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

public class XSSFExample {

    public static void main(String[] args) {
        // Create workbook and sheet
        Workbook wb = new XSSFWorkbook();
        Sheet sheet = wb.createSheet("Sheet 1");

        // Create a row and put some cells in it.
        Row row = sheet.createRow((short) 0);
        row.createCell(0).setCellValue(5.0);
        row.createCell(1).setCellValue(5.0);
        row.createCell(2).setCellFormula("A1/B1");


        // Write the output to a file
        try (FileOutputStream fileOut = new FileOutputStream("XSSFExample.xlsx")) {
            wb.setForceFormulaRecalculation(false);
            System.out.println(wb.getForceFormulaRecalculation()); // prints "false"
            XSSFFormulaEvaluator.evaluateAllFormulaCells((XSSFWorkbook) wb); // this doesn't seem to make any difference
            wb.write(fileOut);
        } catch (IOException ex) {
            Logger.getLogger(XSSFExample.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}
如何创建文件,并且在第一次打开文件后不被提示保存文件

更新:
如上所述,我还尝试了另一种手动重新计算的方法,但没有成功。即使在保存后重新读取该文件,重新读取并另存为第二个文件也不起作用

更新2:
考虑到被接受的答案,我能够通过向上述SSCCE添加以下代码行来解决问题:
(请注意,这只是解决问题的“快速而肮脏”的尝试。可能有很多改进)

ZipFile-ZipFile=new-ZipFile(“XSSFExample.xlsx”);
final ZipOutputStream zos=新的zipoutpstream(新文件输出流(“XSSFExample_NoSave.xlsx”);
对于(枚举e=zipFile.entries();e.hasMoreElements();){
ZipEntry entryIn=(ZipEntry)e.nextElement();
如果(!entryIn.getName().equalsIgnoreCase(“xl/workbook.xml”)){
佐斯·普特内森特里(安特里因);
InputStream=zipFile.getInputStream(entryIn);
字节[]buf=新字节[1024];
内伦;
而((len=(is.read(buf)))>0){
写(buf,0,len);
}
}否则{
putNextEntry(新的ZipEntry(“xl/workbook.xml”);
InputStream=zipFile.getInputStream(entryIn);
字节[]buf=新字节[1024];
内伦;
而(is.read(buf)>0){
字符串s=新字符串(buf);
字符串searchFileVersion=“/relationships\”>问题
问题可能出在MS Excel本身(一旦您确定所有公式都已计算并保存在.xlsx文件中)。根据我的测试,如果Excel发现文件最后一次是由旧版本的Excel或其他应用程序保存的,它将在打开时重新计算所有公式(关键是版本号不匹配和/或低于打开文件的当前Excel版本)以保持良好的兼容性

解决方案 (使Excel认为.xlsx文件是由相同的Excel版本生成的,以避免重新计算)

Excel从.xlsx归档文件(.xlsx只是一个压缩归档文件)内的
xl
目录中的
workbook.xml
文件读取所有文件版本信息

Apache POI生成的workbook.xml文件可能如下所示:

<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <workbookPr date1904="false"/>
  <bookViews><workbookView activeTab="0"/></bookViews>
  <sheets>
    <sheet name="new sheet" r:id="rId3" sheetId="1"/>
  </sheets>
  <calcPr calcId="0"/>
</workbook>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303"/>
  <workbookPr defaultThemeVersion="124226"/>
  <bookViews><workbookView xWindow="630" yWindow="510" windowWidth="27495" windowHeight="14505"/></bookViews>
  <sheets>
    <sheet name="new sheet" sheetId="1" r:id="rId1"/>
  </sheets>
  <calcPr calcId="145621"/>
</workbook>

Excel 2010生成的文件如下所示:

<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <workbookPr date1904="false"/>
  <bookViews><workbookView activeTab="0"/></bookViews>
  <sheets>
    <sheet name="new sheet" r:id="rId3" sheetId="1"/>
  </sheets>
  <calcPr calcId="0"/>
</workbook>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="9303"/>
  <workbookPr defaultThemeVersion="124226"/>
  <bookViews><workbookView xWindow="630" yWindow="510" windowWidth="27495" windowHeight="14505"/></bookViews>
  <sheets>
    <sheet name="new sheet" sheetId="1" r:id="rId1"/>
  </sheets>
  <calcPr calcId="145621"/>
</workbook>

请注意,POI生成的文件中完全缺少
标记,并且在Excel生成的文件中,
calcId
设置为某个实际值的
标记

通过插入相关的
标记,并将
calcId
设置为等于或大于POI生成的
workbook.xml
当前版本Excel生成的数字,我能够避免Excel 2010自动公式重新计算(以及烦人的“保存更改”对话框)。


有关
workbook.xml
格式的更多信息,请访问。

即使我也面临同样的问题,但添加以下行后,问题已得到解决

wb.getCreationHelper().createFormulaEvaluator().evaluateAll();

这确实是一个很好的答案。我相信这将引导我解决这个问题,而且我还了解了Excel在文件版本控制方面的工作原理。现在我正在寻找一种方法来提取
workbook.xml
,编辑它,并将其合并回xlsx。你知道一种正确的方法吗?.xlsx实际上只是一个简单的工具zip archíve(参见此答案)。因此,您应该能够使用任何zip解压缩软件打开它,编辑所需的XML文件并将编辑后的文件放回存档。我想我不清楚我想知道什么。我成功地利用您的信息在Windows中解压、编辑、重新压缩来解决我的问题。我想知道您是否知道使用Java进行解压的正确方法。如果没有,我肯定会找到办法的。我只是不想“重新发明轮子”同时,我找到了一种在Java代码中更改xml的方法。我将用我的解决方案更新我的问题。无论如何,如果您知道更好的方法,我将非常感谢您的建议。看起来POI正在使用CT工作簿实现创建提到的workbook.xml文件(请参阅),您可以直接设置属性,如
XSSFWorkbook.getCTWorkbook().addNewFileVersion();
。在创建default workbook.xml的位置获得灵感。您在哪里添加了它?获得了,我在所有写入操作完成后保存工作簿之前添加了它。