Почему метод Apache POI OPCPackage close () не сохраняет/записывает содержимое для открытия файла с возможностью записи?

используя следующий код, изменения значений ячеек в моей электронной таблице Excel не сохраняются:

OPCPackage pkg = OPCPackage.open(inputFile);
XSSFWorkbook wb = new XSSFWorkbook(pkg);

ModifyWorkbook();

pkg.close();

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

OPCPackage pkg = OPCPackage.open(inputFile);
XSSFWorkbook wb = new XSSFWorkbook(pkg);

ModifyWorkbook();

File tmp = File.createTempFile("tempSpreadsheet", ".xlsx");
FileOutputStream newExcelFile = new FileOutputStream(tmp);
wb.write(newExcelFile);
newExcelFile.close();
tmp.deleteOnExit();
pkg.close();

javadocs и руководства по этому вопросу указывают, что .метод close () должен сохранять и закрывать. Проверки значений измененных ячеек перед закрытием указывают на то, что изменения внесены, но pkg.одного метода close() недостаточно для записи этих изменения в файле при закрытии.

3 ответов


вызов close в книге (XSSF) вызовет OPCP.закрыть который javadoc заявляет:

вызов myWorkbook.close() записать изменения в файл было открыть из.

однако текущая версия (3.15), похоже, имеет проблемы с этим. Сама причина мне неизвестна, но я обнаружил, что призвание myWorkbook.write() зафиксирует не только изменения в выходном потоке, но и в текущем экземпляре после вызова close.

разумное поэтому обходным путем может быть:

try (OutputStream bos = ByteStreams.nullOutputStream()){
    workbook.write(bos);
    workbook.close();
} catch (IOException e) {
    log.error("Could not write Output File", e);
}

здесь сделано с Guavas ByteStreams. Не стесняйтесь использовать другое реализации выходного потока null.


вызов OPCPackage.close() закроет базовую файловую структуру OOXML. Что он не будет делать, это вызвать XSSF (или любой из X??F форматы), чтобы записать свои изменения. Итак, вы напишете неизмененный файл....

Если вы делаете модификации низкого уровня, вы можете открыть OPCPackage, внести изменения, а затем вызвать close, чтобы записать их. Если вы делаете вещи высокого уровня, вы открываете OPCPackage, работаете с кодом UserModel, чтобы внести изменения, а затем спросите usermodel написать вне его изменений. Это последний шаг, который важен.

ваш текущий код-то вроде:

File f = new File("test.txt");
Sting s = readFileCompletely(f);
s = s.replaceAll("foo", "bar");
f.close();
// Why hasn't the file contents changed?

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


в последней версии POI нам не нужно делать это вручную, если мы создаем объект org.апаш.ПОИ.xssf.usermodel.Xssfworkbook по умолчанию конструктор. Прочитайте его метод конструктора по умолчанию

public XSSFWorkbook() {
        super(newPackage());
        onWorkbookCreate();
    }
protected static OPCPackage newPackage() {
        try {
            OPCPackage pkg = OPCPackage.create(new ByteArrayOutputStream());
            // Main part
            PackagePartName corePartName = PackagingURIHelper.createPartName(XSSFRelation.WORKBOOK.getDefaultFileName());
            // Create main part relationship
            pkg.addRelationship(corePartName, TargetMode.INTERNAL, PackageRelationshipTypes.CORE_DOCUMENT);
            // Create main document part
            pkg.createPart(corePartName, XSSFRelation.WORKBOOK.getContentType());

            pkg.getPackageProperties().setCreatorProperty(DOCUMENT_CREATOR);

            return pkg;
        } catch (Exception e){
            throw new POIXMLException(e);
        }
    }