]> source.dussan.org Git - poi.git/commitdiff
[bug-65042] support saving package part data in temp files
authorPJ Fanning <fanningpj@apache.org>
Wed, 13 Oct 2021 18:31:44 +0000 (18:31 +0000)
committerPJ Fanning <fanningpj@apache.org>
Wed, 13 Oct 2021 18:31:44 +0000 (18:31 +0000)
git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1894203 13f79535-47bb-0310-9956-ffa450edef68

poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/PackagePart.java
poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/ZipPackage.java
poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/ZipPackagePart.java
poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/EncryptedTempFilePackagePart.java [new file with mode: 0644]
poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/MemoryPackagePart.java
poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/TempFilePackagePart.java [new file with mode: 0644]
poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFFontData.java
poi-ooxml/src/main/java/org/apache/poi/xslf/usermodel/XSLFObjectData.java

index 801b700316ddbe2ebf13ae432ba92887c3416d12..2aaf907d5eb85868dee718a714bed94aca275f64 100644 (file)
@@ -518,7 +518,7 @@ public abstract class PackagePart implements RelationshipSource, Comparable<Pack
      * @return output stream for this part
      * @see org.apache.poi.openxml4j.opc.internal.MemoryPackagePart
      */
-    public OutputStream getOutputStream() {
+    public OutputStream getOutputStream() throws IOException {
         OutputStream outStream;
         // If this part is a zip package part (read only by design) we convert
         // this part into a MemoryPackagePart instance for write purpose.
@@ -674,9 +674,8 @@ public abstract class PackagePart implements RelationshipSource, Comparable<Pack
      * Method that gets the input stream for this part.
      *
      * @return input stream for this part
-     * @exception IOException
-     *                Throws if an IO Exception occur in the implementation
-     *                method.
+     * @throws IOException
+     *                Throws if an IO Exception occur in the implementation method.
      */
     protected abstract InputStream getInputStreamImpl() throws IOException;
 
@@ -684,8 +683,10 @@ public abstract class PackagePart implements RelationshipSource, Comparable<Pack
      * Method that gets the output stream for this part.
      *
      * @return output stream for this part
+     * @throws IOException
+     *                Throws if an IO Exception occur in the implementation method.
      */
-    protected abstract OutputStream getOutputStreamImpl();
+    protected abstract OutputStream getOutputStreamImpl() throws IOException;
 
     /**
      * Save the content of this part and the associated relationships part (if
index 1ee1961de79a2d699d8f6d778ece8c595037a20f..00cde385054660f91a07d932c6eec81f6dff91e0 100644 (file)
@@ -43,12 +43,7 @@ import org.apache.poi.openxml4j.exceptions.NotOfficeXmlFileException;
 import org.apache.poi.openxml4j.exceptions.ODFNotOfficeXmlFileException;
 import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
 import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException;
-import org.apache.poi.openxml4j.opc.internal.ContentTypeManager;
-import org.apache.poi.openxml4j.opc.internal.FileHelper;
-import org.apache.poi.openxml4j.opc.internal.MemoryPackagePart;
-import org.apache.poi.openxml4j.opc.internal.PartMarshaller;
-import org.apache.poi.openxml4j.opc.internal.ZipContentTypeManager;
-import org.apache.poi.openxml4j.opc.internal.ZipHelper;
+import org.apache.poi.openxml4j.opc.internal.*;
 import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
 import org.apache.poi.openxml4j.util.ZipArchiveThresholdInputStream;
 import org.apache.poi.openxml4j.util.ZipEntrySource;
@@ -63,6 +58,8 @@ import org.apache.poi.util.TempFile;
 public final class ZipPackage extends OPCPackage {
     private static final String MIMETYPE = "mimetype";
     private static final String SETTINGS_XML = "settings.xml";
+    private static boolean useTempFilePackageParts = false;
+    private static boolean encryptTempFilePackageParts = false;
 
     private static final Logger LOG = LogManager.getLogger(ZipPackage.class);
 
@@ -72,6 +69,34 @@ public final class ZipPackage extends OPCPackage {
      */
     private final ZipEntrySource zipArchive;
 
+    /**
+     * @param tempFilePackageParts whether to save package part data in temp files to save memory
+     */
+    public void setUseTempFilePackageParts(boolean tempFilePackageParts) {
+        useTempFilePackageParts = tempFilePackageParts;
+    }
+
+    /**
+     * @param encryptTempFiles whether to encrypt temp files
+     */
+    public void setEncryptTempFilePackageParts(boolean encryptTempFiles) {
+        encryptTempFilePackageParts = encryptTempFiles;
+    }
+
+    /**
+     * @return whether package part data is stored in temp files to save memory
+     */
+    public boolean useTempFilePackageParts() {
+        return useTempFilePackageParts;
+    }
+
+    /**
+     * @return whether package part temp files are encrypted
+     */
+    public boolean encryptTempFilePackageParts() {
+        return encryptTempFilePackageParts;
+    }
+
     /**
      * Constructor. Creates a new, empty ZipPackage.
      */
@@ -371,8 +396,16 @@ public final class ZipPackage extends OPCPackage {
         }
 
         try {
-            return new MemoryPackagePart(this, partName, contentType, loadRelationships);
-        } catch (InvalidFormatException e) {
+            if (useTempFilePackageParts) {
+                if (encryptTempFilePackageParts) {
+                    return new EncryptedTempFilePackagePart(this, partName, contentType, loadRelationships);
+                } else {
+                    return new TempFilePackagePart(this, partName, contentType, loadRelationships);
+                }
+            } else {
+                return new MemoryPackagePart(this, partName, contentType, loadRelationships);
+            }
+        } catch (Exception e) {
             LOG.atWarn().withThrowable(e).log("Failed to create part {}", partName);
             return null;
         }
index fd862a0d7fd11732d3b181929ac28908515366df..3ca7adbe0bbcb61b7b646250180a786b1be4fd72 100644 (file)
@@ -86,7 +86,7 @@ public class ZipPackagePart extends PackagePart {
     /**
      * Get the zip entry of this part.
      *
-     * @return The zip entry in the zip structure coresponding to this part.
+     * @return The zip entry in the zip structure corresponding to this part.
      */
     public ZipArchiveEntry getZipArchive() {
         return zipEntry;
diff --git a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/EncryptedTempFilePackagePart.java b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/EncryptedTempFilePackagePart.java
new file mode 100644 (file)
index 0000000..a9119ea
--- /dev/null
@@ -0,0 +1,144 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc.internal;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
+import org.apache.poi.poifs.crypt.temp.EncryptedTempData;
+import org.apache.poi.util.Beta;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.TempFile;
+
+import java.io.*;
+
+/**
+ * (Experimental) Encrypted Temp File version of a package part.
+ *
+ * @since POI 5.1.0
+ */
+@Beta
+public final class EncryptedTempFilePackagePart extends PackagePart {
+    private static final Logger LOG = LogManager.getLogger(EncryptedTempFilePackagePart.class);
+
+    /**
+     * Storage for the part data.
+     */
+    private EncryptedTempData tempFile;
+
+    /**
+     * Constructor.
+     *
+     * @param pack
+     *            The owner package.
+     * @param partName
+     *            The part name.
+     * @param contentType
+     *            The content type.
+     * @throws InvalidFormatException
+     *             If the specified URI is not OPC compliant.
+     * @throws IOException
+     *             If temp file cannot be created.
+     */
+    public EncryptedTempFilePackagePart(OPCPackage pack, PackagePartName partName,
+                                        String contentType) throws InvalidFormatException, IOException {
+        this(pack, partName, contentType, false);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param pack
+     *            The owner package.
+     * @param partName
+     *            The part name.
+     * @param contentType
+     *            The content type.
+     * @param loadRelationships
+     *            Specify if the relationships will be loaded.
+     * @throws InvalidFormatException
+     *             If the specified URI is not OPC compliant.
+     * @throws IOException
+     *             If temp file cannot be created.
+     */
+    public EncryptedTempFilePackagePart(OPCPackage pack, PackagePartName partName,
+                                        String contentType, boolean loadRelationships)
+            throws InvalidFormatException, IOException {
+        super(pack, partName, new ContentType(contentType), loadRelationships);
+        tempFile = new EncryptedTempData();
+    }
+
+    @Override
+    protected InputStream getInputStreamImpl() throws IOException {
+        return tempFile.getInputStream();
+    }
+
+    @Override
+    protected OutputStream getOutputStreamImpl() throws IOException {
+        return tempFile.getOutputStream();
+    }
+
+    /**
+     * @return EncryptedTempData.getSize() always returns -1
+     */
+    @Override
+    public long getSize() {
+        return -1;
+    }
+
+    @Override
+    public void clear() {
+        try(OutputStream os = getOutputStreamImpl()) {
+            os.write(new byte[0]);
+        } catch (IOException e) {
+            LOG.atWarn().log("Failed to clear data in temp file", e);
+        }
+    }
+
+    @Override
+    public boolean save(OutputStream os) throws OpenXML4JException {
+        return new ZipPartMarshaller().marshall(this, os);
+    }
+
+    @Override
+    public boolean load(InputStream ios) throws InvalidFormatException {
+       try (OutputStream os = getOutputStreamImpl()) {
+            IOUtils.copy(ios, os);
+       } catch(IOException e) {
+            throw new InvalidFormatException(e.getMessage(), e);
+       }
+
+       // All done
+       return true;
+    }
+
+    @Override
+    public void close() {
+        tempFile.dispose();
+    }
+
+    @Override
+    public void flush() {
+        // Do nothing
+    }
+}
index 0454afee023ca1cdde87f769035e7754b82a5c6e..d7f7a2dcf738b8e5ee33b9cf7365d174529b32ba 100644 (file)
@@ -113,17 +113,17 @@ public final class MemoryPackagePart extends PackagePart {
 
     @Override
     public boolean load(InputStream ios) throws InvalidFormatException {
-       try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) {
-           // Grab the data
-          IOUtils.copy(ios, baos);
-           // Save it
-           data = baos.toByteArray();
-       } catch(IOException e) {
-          throw new InvalidFormatException(e.getMessage());
-       }
-
-       // All done
-       return true;
+        try (UnsynchronizedByteArrayOutputStream baos = new UnsynchronizedByteArrayOutputStream()) {
+            // Grab the data
+            IOUtils.copy(ios, baos);
+            // Save it
+            data = baos.toByteArray();
+        } catch (IOException e) {
+            throw new InvalidFormatException(e.getMessage());
+        }
+
+        // All done
+        return true;
     }
 
     @Override
diff --git a/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/TempFilePackagePart.java b/poi-ooxml/src/main/java/org/apache/poi/openxml4j/opc/internal/TempFilePackagePart.java
new file mode 100644 (file)
index 0000000..f3e1fc1
--- /dev/null
@@ -0,0 +1,142 @@
+/* ====================================================================
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+==================================================================== */
+
+package org.apache.poi.openxml4j.opc.internal;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
+import org.apache.poi.openxml4j.exceptions.OpenXML4JException;
+import org.apache.poi.openxml4j.opc.OPCPackage;
+import org.apache.poi.openxml4j.opc.PackagePart;
+import org.apache.poi.openxml4j.opc.PackagePartName;
+import org.apache.poi.openxml4j.opc.internal.marshallers.ZipPartMarshaller;
+import org.apache.poi.util.Beta;
+import org.apache.poi.util.IOUtils;
+import org.apache.poi.util.TempFile;
+
+import java.io.*;
+
+/**
+ * (Experimental) Temp File version of a package part.
+ *
+ * @since POI 5.1.0
+ */
+@Beta
+public final class TempFilePackagePart extends PackagePart {
+    private static final Logger LOG = LogManager.getLogger(TempFilePackagePart.class);
+
+    /**
+     * Storage for the part data.
+     */
+    private File tempFile;
+
+    /**
+     * Constructor.
+     *
+     * @param pack
+     *            The owner package.
+     * @param partName
+     *            The part name.
+     * @param contentType
+     *            The content type.
+     * @throws InvalidFormatException
+     *             If the specified URI is not OPC compliant.
+     * @throws IOException
+     *             If temp file cannot be created.
+     */
+    public TempFilePackagePart(OPCPackage pack, PackagePartName partName,
+                               String contentType) throws InvalidFormatException, IOException {
+        this(pack, partName, contentType, false);
+    }
+
+    /**
+     * Constructor.
+     *
+     * @param pack
+     *            The owner package.
+     * @param partName
+     *            The part name.
+     * @param contentType
+     *            The content type.
+     * @param loadRelationships
+     *            Specify if the relationships will be loaded.
+     * @throws InvalidFormatException
+     *             If the specified URI is not OPC compliant.
+     * @throws IOException
+     *             If temp file cannot be created.
+     */
+    public TempFilePackagePart(OPCPackage pack, PackagePartName partName,
+                               String contentType, boolean loadRelationships)
+            throws InvalidFormatException, IOException {
+        super(pack, partName, new ContentType(contentType), loadRelationships);
+        tempFile = TempFile.createTempFile("poi-package-part", ".tmp");
+    }
+
+    @Override
+    protected InputStream getInputStreamImpl() throws IOException {
+        return new FileInputStream(tempFile);
+    }
+
+    @Override
+    protected OutputStream getOutputStreamImpl() throws IOException {
+        return new FileOutputStream(tempFile);
+    }
+
+    @Override
+    public long getSize() {
+        return tempFile.length();
+    }
+
+    @Override
+    public void clear() {
+        try(OutputStream os = getOutputStreamImpl()) {
+            os.write(new byte[0]);
+        } catch (IOException e) {
+            LOG.atWarn().log("Failed to clear data in temp file", e);
+        }
+    }
+
+    @Override
+    public boolean save(OutputStream os) throws OpenXML4JException {
+        return new ZipPartMarshaller().marshall(this, os);
+    }
+
+    @Override
+    public boolean load(InputStream ios) throws InvalidFormatException {
+       try (OutputStream os = getOutputStreamImpl()) {
+            IOUtils.copy(ios, os);
+       } catch(IOException e) {
+            throw new InvalidFormatException(e.getMessage(), e);
+       }
+
+       // All done
+       return true;
+    }
+
+    @Override
+    public void close() {
+        if (!tempFile.delete()) {
+            LOG.atInfo().log("Failed to delete temp file; may already have been closed and deleted");
+        }
+    }
+
+    @Override
+    public void flush() {
+        // Do nothing
+    }
+}
index 8b2cb09ebc58f9a8f9f30367a394ca1924b5411d..0c2e5efa29f4955eede9afcdc381f9a4eba029e5 100644 (file)
@@ -57,7 +57,7 @@ public class XSLFFontData extends POIXMLDocumentPart {
         return getPackagePart().getInputStream();
     }
 
-    public OutputStream getOutputStream() {
+    public OutputStream getOutputStream() throws IOException {
         final PackagePart pp = getPackagePart();
         pp.clear();
         return pp.getOutputStream();
index 09125fe5850640e6e933aab7ed6ee62e4807c64d..dee50bc1167a66a4d3870582125096d0f840ca89 100644 (file)
@@ -59,7 +59,7 @@ public final class XSLFObjectData extends POIXMLDocumentPart implements ObjectDa
     }
 
     @Override
-    public OutputStream getOutputStream() {
+    public OutputStream getOutputStream() throws IOException {
         final PackagePart pp = getPackagePart();
         pp.clear();
         return pp.getOutputStream();