You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2020/08/08 20:06:07 UTC
[commons-compress] branch master updated: Add Path support to
ZipArchiveOutputStream with an eye on implementing (#123)
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-compress.git
The following commit(s) were added to refs/heads/master by this push:
new dab9c48 Add Path support to ZipArchiveOutputStream with an eye on implementing (#123)
dab9c48 is described below
commit dab9c48811f90c92daf3dd095623f5f18010a553
Author: Gary Gregory <ga...@users.noreply.github.com>
AuthorDate: Sat Aug 8 16:06:00 2020 -0400
Add Path support to ZipArchiveOutputStream with an eye on implementing (#123)
additional ArchiveOutputStream.createArchiveEntry(Path, String,
LinkOption...) methods.
---
.../compress/archivers/ArchiveOutputStream.java | 22 +++++++
.../compress/archivers/zip/ZipArchiveEntry.java | 44 ++++++++++++-
.../archivers/zip/ZipArchiveOutputStream.java | 69 +++++++++++++++----
.../commons/compress/archivers/ZipTestCase.java | 77 +++++++++++++++++++---
4 files changed, 186 insertions(+), 26 deletions(-)
diff --git a/src/main/java/org/apache/commons/compress/archivers/ArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/ArchiveOutputStream.java
index 4377b6d..c7c4a52 100644
--- a/src/main/java/org/apache/commons/compress/archivers/ArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/ArchiveOutputStream.java
@@ -21,6 +21,8 @@ package org.apache.commons.compress.archivers;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
/**
* Archive output stream implementations are expected to override the
@@ -90,6 +92,26 @@ public abstract class ArchiveOutputStream extends OutputStream {
*/
public abstract ArchiveEntry createArchiveEntry(File inputFile, String entryName) throws IOException;
+ /**
+ * Create an archive entry using the inputPath and entryName provided.
+ *
+ * The default implementation calls simply delegates as:
+ * <pre>return createArchiveEntry(inputFile.toFile(), entryName);</pre>
+ *
+ * Subclasses should override this method.
+ *
+ * @param inputPath the file to create the entry from
+ * @param entryName name to use for the entry
+ * @param options options indicating how symbolic links are handled.
+ * @return the ArchiveEntry set up with details from the file
+ *
+ * @throws IOException if an I/O error occurs
+ * @since 1.21
+ */
+ public ArchiveEntry createArchiveEntry(Path inputPath, String entryName, LinkOption... options) throws IOException {
+ return createArchiveEntry(inputPath.toFile(), entryName);
+ }
+
// Generic implementations of OutputStream methods that may be useful to sub-classes
/**
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
index d7aaefb..687615d 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveEntry.java
@@ -17,16 +17,21 @@
*/
package org.apache.commons.compress.archivers.zip;
-import org.apache.commons.compress.archivers.ArchiveEntry;
-import org.apache.commons.compress.archivers.EntryStreamOffsets;
-
import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.Path;
+import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.zip.ZipException;
+import org.apache.commons.compress.archivers.ArchiveEntry;
+import org.apache.commons.compress.archivers.EntryStreamOffsets;
+
/**
* Extension that adds better handling of extra fields and provides
* access to the internal and external file attributes.
@@ -229,6 +234,39 @@ public class ZipArchiveEntry extends java.util.zip.ZipEntry
}
/**
+ * Creates a new zip entry taking some information from the given
+ * path and using the provided name.
+ *
+ * <p>The name will be adjusted to end with a forward slash "/" if
+ * the file is a directory. If the file is not a directory a
+ * potential trailing forward slash will be stripped from the
+ * entry name.</p>
+ * @param inputPath path to create the entry from.
+ * @param entryName name of the entry.
+ * @param options options indicating how symbolic links are handled.
+ * @throws IOException if an I/O error occurs.
+ * @since 1.21
+ */
+ public ZipArchiveEntry(final Path inputPath, final String entryName, LinkOption... options) throws IOException {
+ this(Files.isDirectory(inputPath, options) && !entryName.endsWith("/") ?
+ entryName + "/" : entryName);
+ if (Files.isRegularFile(inputPath, options)){
+ setSize(Files.size(inputPath));
+ }
+ setTime(Files.getLastModifiedTime(inputPath, options));
+ // TODO are there any other fields we can set here?
+ }
+
+ /**
+ * Sets the modification time of the entry.
+ * @param fileTime the entry modification time.
+ * @since 1.21
+ */
+ public void setTime(final FileTime fileTime) {
+ setTime(fileTime.toMillis());
+ }
+
+ /**
* Overwrite clone.
* @return a cloned copy of this ZipArchiveEntry
*/
diff --git a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
index 16ad3b7..a926531 100644
--- a/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
+++ b/src/main/java/org/apache/commons/compress/archivers/zip/ZipArchiveOutputStream.java
@@ -17,6 +17,18 @@
*/
package org.apache.commons.compress.archivers.zip;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.DATA_DESCRIPTOR_MIN_VERSION;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.DEFLATE_MIN_VERSION;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.INITIAL_VERSION;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.SHORT;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC_SHORT;
+import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MIN_VERSION;
+import static org.apache.commons.compress.archivers.zip.ZipLong.putLong;
+import static org.apache.commons.compress.archivers.zip.ZipShort.putShort;
+
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
@@ -26,6 +38,9 @@ import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
+import java.nio.file.LinkOption;
+import java.nio.file.OpenOption;
+import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Calendar;
import java.util.EnumSet;
@@ -40,18 +55,6 @@ import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.ArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.DATA_DESCRIPTOR_MIN_VERSION;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.DEFLATE_MIN_VERSION;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.DWORD;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.INITIAL_VERSION;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.SHORT;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.WORD;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MAGIC_SHORT;
-import static org.apache.commons.compress.archivers.zip.ZipConstants.ZIP64_MIN_VERSION;
-import static org.apache.commons.compress.archivers.zip.ZipLong.putLong;
-import static org.apache.commons.compress.archivers.zip.ZipShort.putShort;
-
/**
* Reimplementation of {@link java.util.zip.ZipOutputStream
* java.util.zip.ZipOutputStream} that does handle the extended
@@ -307,12 +310,24 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
* @throws IOException on error
*/
public ZipArchiveOutputStream(final File file) throws IOException {
+ this(file.toPath());
+ }
+
+ /**
+ * Creates a new ZIP OutputStream writing to a Path. Will use
+ * random access if possible.
+ * @param file the file to zip to
+ * @param options options specifying how the file is opened.
+ * @throws IOException on error
+ * @since 1.21
+ */
+ public ZipArchiveOutputStream(final Path file, OpenOption... options) throws IOException {
def = new Deflater(level, true);
OutputStream o = null;
SeekableByteChannel _channel = null;
StreamCompressor _streamCompressor = null;
try {
- _channel = Files.newByteChannel(file.toPath(),
+ _channel = Files.newByteChannel(file,
EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE,
StandardOpenOption.READ,
StandardOpenOption.TRUNCATE_EXISTING));
@@ -321,7 +336,7 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
} catch (final IOException e) { // NOSONAR
IOUtils.closeQuietly(_channel);
_channel = null;
- o = new FileOutputStream(file);
+ o = Files.newOutputStream(file, options);
_streamCompressor = StreamCompressor.create(o, def);
}
out = o;
@@ -1758,6 +1773,32 @@ public class ZipArchiveOutputStream extends ArchiveOutputStream {
}
/**
+ * Creates a new zip entry taking some information from the given
+ * file and using the provided name.
+ *
+ * <p>The name will be adjusted to end with a forward slash "/" if
+ * the file is a directory. If the file is not a directory a
+ * potential trailing forward slash will be stripped from the
+ * entry name.</p>
+ *
+ * <p>Must not be used if the stream has already been closed.</p>
+ * @param inputPath path to create the entry from.
+ * @param entryName name of the entry.
+ * @param options options indicating how symbolic links are handled.
+ * @return a new instance.
+ * @throws IOException if an I/O error occurs.
+ * @since 1.21
+ */
+ @Override
+ public ArchiveEntry createArchiveEntry(final Path inputPath, final String entryName, LinkOption... options)
+ throws IOException {
+ if (finished) {
+ throw new IOException("Stream has already been finished");
+ }
+ return new ZipArchiveEntry(inputPath, entryName);
+ }
+
+ /**
* Get the existing ZIP64 extended information extra field or
* create a new one and add it to the entry.
*
diff --git a/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java b/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
index e66a30a..dcb61fd 100644
--- a/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
+++ b/src/test/java/org/apache/commons/compress/archivers/ZipTestCase.java
@@ -18,7 +18,13 @@
*/
package org.apache.commons.compress.archivers;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.File;
@@ -30,6 +36,7 @@ import java.io.OutputStream;
import java.nio.channels.Channels;
import java.nio.channels.SeekableByteChannel;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
@@ -498,14 +505,16 @@ public final class ZipTestCase extends AbstractTestCase {
ZipArchiveOutputStream zos = null;
ZipFile zf = null;
FileInputStream fis = null;
+ File tmpDir = tmp[0];
+ File tmpFile = tmp[1];
try {
- archive = File.createTempFile("test.", ".zip", tmp[0]);
+ archive = File.createTempFile("test.", ".zip", tmpDir);
archive.deleteOnExit();
zos = new ZipArchiveOutputStream(archive);
- final ZipArchiveEntry in = new ZipArchiveEntry(tmp[1], "foo");
+ final ZipArchiveEntry in = new ZipArchiveEntry(tmpFile, "foo");
zos.putArchiveEntry(in);
- final byte[] b = new byte[(int) tmp[1].length()];
- fis = new FileInputStream(tmp[1]);
+ final byte[] b = new byte[(int) tmpFile.length()];
+ fis = new FileInputStream(tmpFile);
while (fis.read(b) > 0) {
zos.write(b);
}
@@ -518,8 +527,8 @@ public final class ZipTestCase extends AbstractTestCase {
final ZipArchiveEntry out = zf.getEntry("foo");
assertNotNull(out);
assertEquals("foo", out.getName());
- assertEquals(tmp[1].length(), out.getSize());
- assertEquals(tmp[1].lastModified() / 2000,
+ assertEquals(tmpFile.length(), out.getSize());
+ assertEquals(tmpFile.lastModified() / 2000,
out.getLastModifiedDate().getTime() / 2000);
assertFalse(out.isDirectory());
} finally {
@@ -531,8 +540,58 @@ public final class ZipTestCase extends AbstractTestCase {
if (fis != null) {
fis.close();
}
- tryHardToDelete(tmp[1]);
- rmdir(tmp[0]);
+ tryHardToDelete(tmpFile);
+ rmdir(tmpDir);
+ }
+ }
+
+ @Test
+ public void testZipArchiveEntryNewFromPath() throws Exception {
+ final File[] tmp = createTempDirAndFile();
+ File archiveFile = null;
+ Path archivePath = null;
+ ZipArchiveOutputStream zos = null;
+ ZipFile zf = null;
+ FileInputStream fis = null;
+ File tmpDir = tmp[0];
+ File tmpFile = tmp[1];
+ Path tmpFilePath = tmpFile.toPath();
+ try {
+ archiveFile = File.createTempFile("test.", ".zip", tmpDir);
+ archivePath = archiveFile.toPath();
+ archiveFile.deleteOnExit();
+ zos = new ZipArchiveOutputStream(archivePath);
+ final ZipArchiveEntry in = (ZipArchiveEntry) zos.createArchiveEntry(tmpFilePath, "foo");
+ zos.putArchiveEntry(in);
+ final byte[] b = new byte[(int) tmpFile.length()];
+ fis = new FileInputStream(tmpFile);
+ while (fis.read(b) > 0) {
+ zos.write(b);
+ }
+ fis.close();
+ fis = null;
+ zos.closeArchiveEntry();
+ zos.close();
+ zos = null;
+ zf = new ZipFile(archiveFile);
+ final ZipArchiveEntry out = zf.getEntry("foo");
+ assertNotNull(out);
+ assertEquals("foo", out.getName());
+ assertEquals(tmpFile.length(), out.getSize());
+ assertEquals(tmpFile.lastModified() / 2000,
+ out.getLastModifiedDate().getTime() / 2000);
+ assertFalse(out.isDirectory());
+ } finally {
+ ZipFile.closeQuietly(zf);
+ if (zos != null) {
+ zos.close();
+ }
+ tryHardToDelete(archiveFile);
+ if (fis != null) {
+ fis.close();
+ }
+ tryHardToDelete(tmpFile);
+ rmdir(tmpDir);
}
}