You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by kr...@apache.org on 2014/12/18 21:51:57 UTC

svn commit: r1646531 - in /commons/proper/compress/trunk: ./ src/main/java/org/apache/commons/compress/archivers/zip/ src/test/java/org/apache/commons/compress/ src/test/java/org/apache/commons/compress/archivers/

Author: krosenvold
Date: Thu Dec 18 20:51:57 2014
New Revision: 1646531

URL: http://svn.apache.org/r1646531
Log:
COMPRESS-295 Add support for transferring a zip entry from one zip file to another

Added:
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryPredicate.java
Modified:
    commons/proper/compress/trunk/.gitignore
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
    commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
    commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/AbstractTestCase.java
    commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java

Modified: commons/proper/compress/trunk/.gitignore
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/.gitignore?rev=1646531&r1=1646530&r2=1646531&view=diff
==============================================================================
--- commons/proper/compress/trunk/.gitignore (original)
+++ commons/proper/compress/trunk/.gitignore Thu Dec 18 20:51:57 2014
@@ -2,4 +2,6 @@ target
 .project
 .classpath
 .settings
+.idea
+*.iml
 *~

Added: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryPredicate.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryPredicate.java?rev=1646531&view=auto
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryPredicate.java (added)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntryPredicate.java Thu Dec 18 20:51:57 2014
@@ -0,0 +1,32 @@
+/*
+ *  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.commons.compress.archivers.zip;
+
+/**
+ *  A predicate to test if a #ZipArchiveEntry matches a criteria.
+ *  Some day this can extend java.util.function.Predicate
+ */
+public interface ZipArchiveEntryPredicate {
+    /**
+     * Indicate if the given entry should be included in the operation
+     * @param zipArchiveEntry the entry to test
+     * @return true if the entry should be included
+     */
+    boolean test(ZipArchiveEntry zipArchiveEntry);
+}

Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java?rev=1646531&r1=1646530&r2=1646531&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java Thu Dec 18 20:51:57 2014
@@ -20,9 +20,11 @@ package org.apache.commons.compress.arch
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.IOException;
+import java.io.InputStream;
 import java.io.OutputStream;
 import java.io.RandomAccessFile;
 import java.nio.ByteBuffer;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -432,27 +434,36 @@ public class ZipArchiveOutputStream exte
      */
     @Override
     public void closeArchiveEntry() throws IOException {
-        if (finished) {
-            throw new IOException("Stream has already been finished");
-        }
-
-        if (entry == null) {
-            throw new IOException("No current entry to close");
-        }
-
-        if (!entry.hasWritten) {
-            write(EMPTY, 0, 0);
-        }
+        preClose();
 
         flushDeflater();
 
-        final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry);
         long bytesWritten = written - entry.dataStart;
         long realCrc = crc.getValue();
         crc.reset();
 
+        doCloseEntry(realCrc, bytesWritten);
+    }
+
+    /**
+     * Writes all necessary data for this entry.
+     *
+     * @throws IOException            on error
+     * @throws Zip64RequiredException if the entry's uncompressed or
+     *                                compressed size exceeds 4 GByte and {@link #setUseZip64}
+     *                                is {@link Zip64Mode#Never}.
+     */
+    private void closeCopiedEntry() throws IOException {
+        preClose();
+        long realCrc = entry.entry.getCrc();
+        entry.bytesRead = entry.entry.getSize();
+        doCloseEntry(realCrc, entry.entry.getCompressedSize());
+    }
+
+    private void doCloseEntry(long realCrc, long bytesWritten) throws IOException {
+        final Zip64Mode effectiveMode = getEffectiveZip64Mode(entry.entry);
         final boolean actuallyNeedsZip64 =
-            handleSizesAndCrc(bytesWritten, realCrc, effectiveMode);
+                handleSizesAndCrc(bytesWritten, realCrc, effectiveMode);
 
         if (raf != null) {
             rewriteSizesAndCrc(actuallyNeedsZip64);
@@ -462,6 +473,37 @@ public class ZipArchiveOutputStream exte
         entry = null;
     }
 
+    private void preClose() throws IOException {
+        if (finished) {
+            throw new IOException("Stream has already been finished");
+        }
+
+        if (entry == null) {
+            throw new IOException("No current entry to close");
+        }
+
+        if (!entry.hasWritten) {
+            write(EMPTY, 0, 0);
+        }
+    }
+
+    /**
+     * Adds an archive entry with a raw input stream.
+     *
+     * The entry is put and closed immediately.
+     *
+     * @param entry The archive entry to add
+     * @param rawStream The raw input stream of a different entry. May be compressed/encrypted.
+     * @throws IOException If copying fails
+     */
+    public void addRawArchiveEntry(ZipArchiveEntry entry, InputStream rawStream)
+            throws IOException {
+        ZipArchiveEntry ae = new ZipArchiveEntry((java.util.zip.ZipEntry)entry);
+        putArchiveEntry(ae);
+        copyFromZipInputStream(rawStream);
+        closeCopiedEntry();
+    }
+
     /**
      * Ensures all bytes sent to the deflater are written to the stream.
      */
@@ -768,6 +810,25 @@ public class ZipArchiveOutputStream exte
         count(length);
     }
 
+    private void copyFromZipInputStream(InputStream src) throws IOException {
+        if (entry == null) {
+            throw new IllegalStateException("No current entry");
+        }
+        ZipUtil.checkRequestedFeatures(entry.entry);
+        entry.hasWritten = true;
+        byte[] tmpBuf = new byte[4096];
+        int length = src.read( tmpBuf );
+        while ( length >= 0 )
+        {
+            writeOut( tmpBuf, 0, length );
+            written += length;
+            crc.update( tmpBuf, 0, length );
+
+            count( length );
+            length = src.read( tmpBuf );
+        }
+    }
+
     /**
      * write implementation for DEFLATED entries.
      */

Modified: commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java?rev=1646531&r1=1646530&r2=1646531&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java (original)
+++ commons/proper/compress/trunk/src/main/java/org/apache/commons/compress/archivers/zip/ZipFile.java Thu Dec 18 20:51:57 2014
@@ -346,6 +346,44 @@ public class ZipFile implements Closeabl
     }
 
     /**
+     * Expose the raw stream of the archive entry (compressed form)
+     * <p/>
+     * This method does not relate to how/if we understand the payload in the
+     * stream, since we really only intend to move it on to somewhere else.
+     *
+     * @param ze The entry to get the stream for
+     * @return The raw input stream containing (possibly) compressed data.
+     */
+    private InputStream getRawInputStream(ZipArchiveEntry ze) {
+        if (!(ze instanceof Entry)) {
+            return null;
+        }
+        OffsetEntry offsetEntry = ((Entry) ze).getOffsetEntry();
+        long start = offsetEntry.dataOffset;
+        return new BoundedInputStream(start, ze.getCompressedSize());
+    }
+
+
+    /**
+     * Transfer selected entries from this zipfile to a given #ZipArchiveOutputStream.
+     * Compression and all other attributes will be as in this file.
+     * This method transfers entries based on the central directory of the zip file.
+     *
+     * @param target The zipArchiveOutputStream to write the entries to
+     * @param predicate A predicate that selects which entries to write
+     */
+    public void copyRawEntries(ZipArchiveOutputStream target, ZipArchiveEntryPredicate predicate)
+            throws IOException {
+        Enumeration<ZipArchiveEntry> src = getEntriesInPhysicalOrder();
+        while (src.hasMoreElements()) {
+            ZipArchiveEntry entry = src.nextElement();
+            if (predicate.test( entry)) {
+                target.addRawArchiveEntry(entry, getRawInputStream(entry));
+            }
+        }
+    }
+
+    /**
      * Returns an InputStream for reading the contents of the given entry.
      *
      * @param ze the entry to get the stream for.

Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/AbstractTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/AbstractTestCase.java?rev=1646531&r1=1646530&r2=1646531&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/AbstractTestCase.java (original)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/AbstractTestCase.java Thu Dec 18 20:51:57 2014
@@ -392,8 +392,7 @@ public abstract class AbstractTestCase e
      * element of the two element array).
      */
     protected File[] createTempDirAndFile() throws IOException {
-        File tmpDir = mkdir("testdir");
-        tmpDir.deleteOnExit();
+        File tmpDir = createTempDir();
         File tmpFile = File.createTempFile("testfile", "", tmpDir);
         tmpFile.deleteOnExit();
         FileOutputStream fos = new FileOutputStream(tmpFile);
@@ -405,6 +404,12 @@ public abstract class AbstractTestCase e
         }
     }
 
+    protected File createTempDir() throws IOException {
+        File tmpDir = mkdir("testdir");
+        tmpDir.deleteOnExit();
+        return tmpDir;
+    }
+
     protected void closeQuietly(Closeable closeable){
         if (closeable != null) {
             try {

Modified: commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
URL: http://svn.apache.org/viewvc/commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java?rev=1646531&r1=1646530&r2=1646531&view=diff
==============================================================================
--- commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java (original)
+++ commons/proper/compress/trunk/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java Thu Dec 18 20:51:57 2014
@@ -18,22 +18,14 @@
  */
 package org.apache.commons.compress.archivers;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.compress.AbstractTestCase;
-import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
-import org.apache.commons.compress.archivers.zip.ZipArchiveInputStream;
-import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
-import org.apache.commons.compress.archivers.zip.ZipFile;
-import org.apache.commons.compress.archivers.zip.ZipMethod;
+import org.apache.commons.compress.archivers.zip.*;
 import org.apache.commons.compress.utils.IOUtils;
+import org.junit.Assert;
 
 public final class ZipTestCase extends AbstractTestCase {
     /**
@@ -293,6 +285,82 @@ public final class ZipTestCase extends A
             rmdir(tmp[0]);
         }
     }
+    String first_payload = "ABBA";
+    String second_payload = "AAAAAAAAAAAA";
+    ZipArchiveEntryPredicate allFilesPredicate = new ZipArchiveEntryPredicate() {
+        public boolean test(ZipArchiveEntry zipArchiveEntry) {
+            return true;
+        }
+    };
+
+
+    public void testCopyRawEntriesFromFile
+            ()
+            throws IOException {
+
+        File[] tmp = createTempDirAndFile();
+        File reference = createReferenceFile(tmp[0]);
+
+        File a1 = File.createTempFile("src1.", ".zip", tmp[0]);
+        createFirstEntry(new ZipArchiveOutputStream(a1)).close();
+
+        File a2 = File.createTempFile("src2.", ".zip", tmp[0]);
+        createSecondEntry(new ZipArchiveOutputStream(a2)).close();
+
+        ZipFile zf1 = new ZipFile(a1);
+        ZipFile zf2 = new ZipFile(a2);
+        File fileResult = File.createTempFile("file-actual.", ".zip", tmp[0]);
+        ZipArchiveOutputStream zos2 = new ZipArchiveOutputStream(fileResult);
+        zf1.copyRawEntries(zos2, allFilesPredicate);
+        zf2.copyRawEntries(zos2, allFilesPredicate);
+        zos2.close();
+        assertSameFileContents(reference, fileResult);
+        zf1.close();
+        zf2.close();
+    }
+
+    private File createReferenceFile(File directory) throws IOException {
+        File reference = File.createTempFile("expected.", ".zip", directory);
+        ZipArchiveOutputStream zos = new ZipArchiveOutputStream(reference);
+        createFirstEntry(zos);
+        createSecondEntry(zos);
+        zos.close();
+        return reference;
+    }
+
+    private ZipArchiveOutputStream createFirstEntry(ZipArchiveOutputStream zos) throws IOException {
+        createArchiveEntry(first_payload, zos, "file1.txt");
+        return zos;
+    }
+
+    private ZipArchiveOutputStream createSecondEntry(ZipArchiveOutputStream zos) throws IOException {
+        createArchiveEntry(second_payload, zos, "file2.txt");
+        return zos;
+    }
+
+
+    private void assertSameFileContents(File expectedFile, File actualFile) throws IOException {
+        int size = (int) Math.max(expectedFile.length(), actualFile.length());
+        byte[] expected = new byte[size];
+        byte[] actual = new byte[size];
+        final FileInputStream expectedIs = new FileInputStream(expectedFile);
+        final FileInputStream actualIs = new FileInputStream(actualFile);
+        IOUtils.readFully(expectedIs, expected);
+        IOUtils.readFully(actualIs, actual);
+        expectedIs.close();
+        actualIs.close();
+        Assert.assertArrayEquals(expected, actual);
+    }
+
+
+    private void createArchiveEntry(String payload, ZipArchiveOutputStream zos, String name)
+            throws IOException {
+        ZipArchiveEntry in = new ZipArchiveEntry(name);
+        zos.putArchiveEntry(in);
+
+        zos.write(payload.getBytes());
+        zos.closeArchiveEntry();
+    }
 
     public void testFileEntryFromFile() throws Exception {
         File[] tmp = createTempDirAndFile();



Re: svn commit: r1646531 - in /commons/proper/compress/trunk: ./ src/main/java/org/apache/commons/compress/archivers/zip/ src/test/java/org/apache/commons/compress/ src/test/java/org/apache/commons/compress/archivers/

Posted by Kristian Rosenvold <kr...@gmail.com>.
I'll put taking a look at addRawArchiveEntry  with ZipEntry on my todo
list. I have quite a few threads converging into the last class
already; but everything should be done in a few days now.

copyRawEntries could definitely be somewhere else. Originally I
implemented it on ZipArchiveOutputStream, but it's really not that
much better :)

Kristian


2014-12-23 18:43 GMT+01:00 Stefan Bodewig <bo...@apache.org>:
> On 2014-12-18, <kr...@apache.org> wrote:
>
>>>    public void addRawArchiveEntry(ZipArchiveEntry entry, InputStream rawStream)
>
> Technically entry could be a java.util.zip.ZipEntry, I'm not sure this
> would open up new opportunities, though.
>
>>>    /**
>>>     * Transfer selected entries from this zipfile to a given #ZipArchiveOutputStream.
>>>     * Compression and all other attributes will be as in this file.
>>>     * This method transfers entries based on the central directory of the zip file.
>>>     *
>>>     * @param target The zipArchiveOutputStream to write the entries to
>>>     * @param predicate A predicate that selects which entries to write
>>>     */
>>>    public void copyRawEntries(ZipArchiveOutputStream target, ZipArchiveEntryPredicate predicate)
>>>            throws IOException {
>
> I'm not entirely sure this should be an instance method of ZipFile, it
> looks more like a utility method that could live outside of the class as
> it doesn't need any of the non-public parts of it.
>
> Then again ZipUtil is a bag of pretty unrelated methods so it doesn't
> sound like a happy place for it either.
>
> Stefan
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
> For additional commands, e-mail: dev-help@commons.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org


Re: svn commit: r1646531 - in /commons/proper/compress/trunk: ./ src/main/java/org/apache/commons/compress/archivers/zip/ src/test/java/org/apache/commons/compress/ src/test/java/org/apache/commons/compress/archivers/

Posted by Stefan Bodewig <bo...@apache.org>.
On 2014-12-18, <kr...@apache.org> wrote:

>>    public void addRawArchiveEntry(ZipArchiveEntry entry, InputStream rawStream)

Technically entry could be a java.util.zip.ZipEntry, I'm not sure this
would open up new opportunities, though.

>>    /**
>>     * Transfer selected entries from this zipfile to a given #ZipArchiveOutputStream.
>>     * Compression and all other attributes will be as in this file.
>>     * This method transfers entries based on the central directory of the zip file.
>>     *
>>     * @param target The zipArchiveOutputStream to write the entries to
>>     * @param predicate A predicate that selects which entries to write
>>     */
>>    public void copyRawEntries(ZipArchiveOutputStream target, ZipArchiveEntryPredicate predicate)
>>            throws IOException {

I'm not entirely sure this should be an instance method of ZipFile, it
looks more like a utility method that could live outside of the class as
it doesn't need any of the non-public parts of it.

Then again ZipUtil is a bag of pretty unrelated methods so it doesn't
sound like a happy place for it either.

Stefan

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@commons.apache.org
For additional commands, e-mail: dev-help@commons.apache.org