Skip to content

Commit

Permalink
Merge 7510885 into a1d8e2a
Browse files Browse the repository at this point in the history
  • Loading branch information
PostelnicuGeorge committed Dec 23, 2021
2 parents a1d8e2a + 7510885 commit 3adeef0
Show file tree
Hide file tree
Showing 13 changed files with 897 additions and 96 deletions.
Expand Up @@ -17,13 +17,11 @@
*/
package org.apache.commons.compress.archivers.zip;

import org.apache.commons.compress.parallel.FileBasedScatterGatherBackingStore;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.compress.parallel.ScatterGatherBackingStore;
import org.apache.commons.compress.parallel.ScatterGatherBackingStoreSupplier;
import org.apache.commons.compress.parallel.*;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Deque;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedDeque;
Expand Down Expand Up @@ -66,7 +64,7 @@ private static class DefaultBackingStoreSupplier implements ScatterGatherBacking

@Override
public ScatterGatherBackingStore get() throws IOException {
final File tempFile = File.createTempFile("parallelscatter", "n" + storeNum.incrementAndGet());
final Path tempFile = Files.createTempFile("parallelscatter", "n" + storeNum.incrementAndGet());
return new FileBasedScatterGatherBackingStore(tempFile);
}
}
Expand Down
Expand Up @@ -27,6 +27,7 @@
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Path;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
Expand Down Expand Up @@ -186,7 +187,19 @@ public void close() throws IOException {
* @throws FileNotFoundException if the file cannot be found
*/
public static ScatterZipOutputStream fileBased(final File file) throws FileNotFoundException {
return fileBased(file, Deflater.DEFAULT_COMPRESSION);
return pathBased(file.toPath(), Deflater.DEFAULT_COMPRESSION);
}

/**
* Create a {@link ScatterZipOutputStream} with default compression level that is backed by a file
*
* @param path The path to offload compressed data into.
* @return A ScatterZipOutputStream that is ready for use.
* @throws FileNotFoundException if the path cannot be found
* @since 1.22
*/
public static ScatterZipOutputStream pathBased(final Path path) throws FileNotFoundException {
return pathBased(path, Deflater.DEFAULT_COMPRESSION);
}

/**
Expand All @@ -198,7 +211,20 @@ public static ScatterZipOutputStream fileBased(final File file) throws FileNotFo
* @throws FileNotFoundException if the file cannot be found
*/
public static ScatterZipOutputStream fileBased(final File file, final int compressionLevel) throws FileNotFoundException {
final ScatterGatherBackingStore bs = new FileBasedScatterGatherBackingStore(file);
return pathBased(file.toPath(), compressionLevel);
}

/**
* Create a {@link ScatterZipOutputStream} that is backed by a file
*
* @param path The path to offload compressed data into.
* @param compressionLevel The compression level to use, @see #Deflater
* @return A ScatterZipOutputStream that is ready for use.
* @throws FileNotFoundException if the path cannot be found
* @since 1.22
*/
public static ScatterZipOutputStream pathBased(final Path path, final int compressionLevel) throws FileNotFoundException {
final ScatterGatherBackingStore bs = new FileBasedScatterGatherBackingStore(path);
// lifecycle is bound to the ScatterZipOutputStream returned
final StreamCompressor sc = StreamCompressor.create(compressionLevel, bs); //NOSONAR
return new ScatterZipOutputStream(bs, sc);
Expand Down
Expand Up @@ -365,8 +365,33 @@ public ZipArchiveOutputStream(final Path file, final OpenOption... options) thro
* @since 1.20
*/
public ZipArchiveOutputStream(final File file, final long zipSplitSize) throws IOException {
this(file.toPath(), zipSplitSize);
}

/**
* Creates a split ZIP Archive.
*
* <p>The files making up the archive will use Z01, Z02,
* ... extensions and the last part of it will be the given {@code
* file}.</p>
*
* <p>Even though the stream writes to a file this stream will
* behave as if no random access was possible. This means the
* sizes of stored entries need to be known before the actual
* entry data is written.</p>
*
* @param path the path to the file that will become the last part of the split archive
* @param zipSplitSize maximum size of a single part of the split
* archive created by this stream. Must be between 64kB and about
* 4GB.
*
* @throws IOException on error
* @throws IllegalArgumentException if zipSplitSize is not in the required range
* @since 1.22
*/
public ZipArchiveOutputStream(final Path path, final long zipSplitSize) throws IOException {
def = new Deflater(level, true);
this.out = new ZipSplitOutputStream(file, zipSplitSize);
this.out = new ZipSplitOutputStream(path, zipSplitSize);
streamCompressor = StreamCompressor.create(this.out, def);
channel = null;
isSplitZip = true;
Expand Down Expand Up @@ -518,6 +543,11 @@ public void setUseZip64(final Zip64Mode mode) {
zip64Mode = mode;
}

@Override
public long getBytesWritten() {
return streamCompressor.getTotalBytesWritten();
}

/**
* {@inheritDoc}
* @throws Zip64RequiredException if the archive's size exceeds 4
Expand Down
Expand Up @@ -29,6 +29,7 @@
import java.nio.channels.FileChannel;
import java.nio.channels.SeekableByteChannel;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.Collections;
Expand Down Expand Up @@ -174,6 +175,17 @@ public ZipFile(final File f) throws IOException {
this(f, ZipEncodingHelper.UTF8);
}

/**
* Opens the given path for reading, assuming "UTF8" for file names.
*
* @param path path to the archive.
*
* @throws IOException if an error occurs while reading the file.
*/
public ZipFile(final Path path) throws IOException {
this(path, ZipEncodingHelper.UTF8);
}

/**
* Opens the given file for reading, assuming "UTF8".
*
Expand All @@ -182,7 +194,7 @@ public ZipFile(final File f) throws IOException {
* @throws IOException if an error occurs while reading the file.
*/
public ZipFile(final String name) throws IOException {
this(new File(name), ZipEncodingHelper.UTF8);
this(new File(name).toPath(), ZipEncodingHelper.UTF8);
}

/**
Expand All @@ -196,7 +208,7 @@ public ZipFile(final String name) throws IOException {
* @throws IOException if an error occurs while reading the file.
*/
public ZipFile(final String name, final String encoding) throws IOException {
this(new File(name), encoding, true);
this(new File(name).toPath(), encoding, true);
}

/**
Expand All @@ -210,7 +222,22 @@ public ZipFile(final String name, final String encoding) throws IOException {
* @throws IOException if an error occurs while reading the file.
*/
public ZipFile(final File f, final String encoding) throws IOException {
this(f, encoding, true);
this(f.toPath(), encoding, true);
}

/**
* Opens the given path for reading, assuming the specified
* encoding for file names and scanning for unicode extra fields.
*
* @param path path to the archive.
* @param encoding the encoding to use for file names, use null
* for the platform's default encoding
*
* @throws IOException if an error occurs while reading the file.
* @since 1.22
*/
public ZipFile(final Path path, final String encoding) throws IOException {
this(path, encoding, true);
}

/**
Expand All @@ -227,7 +254,25 @@ public ZipFile(final File f, final String encoding) throws IOException {
*/
public ZipFile(final File f, final String encoding, final boolean useUnicodeExtraFields)
throws IOException {
this(f, encoding, useUnicodeExtraFields, false);
this(f.toPath(), encoding, useUnicodeExtraFields, false);
}

/**
* Opens the given path for reading, assuming the specified
* encoding for file names.
*
* @param path path to the archive.
* @param encoding the encoding to use for file names, use null
* for the platform's default encoding
* @param useUnicodeExtraFields whether to use InfoZIP Unicode
* Extra Fields (if present) to set the file names.
*
* @throws IOException if an error occurs while reading the file.
* @since 1.22
*/
public ZipFile(final Path path, final String encoding, final boolean useUnicodeExtraFields)
throws IOException {
this(path, encoding, useUnicodeExtraFields, false);
}

/**
Expand Down Expand Up @@ -262,6 +307,39 @@ public ZipFile(final File f, final String encoding, final boolean useUnicodeExtr
f.getAbsolutePath(), encoding, useUnicodeExtraFields, true, ignoreLocalFileHeader);
}

/**
* Opens the given path for reading, assuming the specified
* encoding for file names.
*
*
* <p>By default the central directory record and all local file headers of the archive will be read immediately
* which may take a considerable amount of time when the archive is big. The {@code ignoreLocalFileHeader} parameter
* can be set to {@code true} which restricts parsing to the central directory. Unfortunately the local file header
* may contain information not present inside of the central directory which will not be available when the argument
* is set to {@code true}. This includes the content of the Unicode extra field, so setting {@code
* ignoreLocalFileHeader} to {@code true} means {@code useUnicodeExtraFields} will be ignored effectively. Also
* {@link #getRawInputStream} is always going to return {@code null} if {@code ignoreLocalFileHeader} is {@code
* true}.</p>
*
* @param path path to the archive.
* @param encoding the encoding to use for file names, use null
* for the platform's default encoding
* @param useUnicodeExtraFields whether to use InfoZIP Unicode
* Extra Fields (if present) to set the file names.
* @param ignoreLocalFileHeader whether to ignore information
* stored inside the local file header (see the notes in this method's javadoc)
*
* @throws IOException if an error occurs while reading the file.
* @since 1.22
*/
public ZipFile(final Path path, final String encoding, final boolean useUnicodeExtraFields,
final boolean ignoreLocalFileHeader)
throws IOException {
this(Files.newByteChannel(path, EnumSet.of(StandardOpenOption.READ)),
path.toAbsolutePath().toString(), encoding, useUnicodeExtraFields,
true, ignoreLocalFileHeader);
}

/**
* Opens the given channel for reading, assuming "UTF8" for file names.
*
Expand Down
Expand Up @@ -22,7 +22,8 @@
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.*;
import java.util.Objects;

/**
* Used internally by {@link ZipArchiveOutputStream} when creating a split archive.
Expand All @@ -31,7 +32,7 @@
*/
class ZipSplitOutputStream extends OutputStream {
private OutputStream outputStream;
private File zipFile;
private Path zipFile;
private final long splitSize;
private int currentSplitSegmentIndex;
private long currentSplitSegmentBytesWritten;
Expand All @@ -54,22 +55,35 @@ class ZipSplitOutputStream extends OutputStream {
* then there will only be one split zip, and its suffix is .zip,
* otherwise the split segments should be like .z01, .z02, ... .z(N-1), .zip
*
* @param zipFile the zip file to write to
* @param zipFile the path to zip file to write to
* @param splitSize the split size
* @since 1.22
*/
public ZipSplitOutputStream(final File zipFile, final long splitSize) throws IllegalArgumentException, IOException {
public ZipSplitOutputStream(final Path zipFile, final long splitSize) throws IllegalArgumentException, IOException {
if (splitSize < ZIP_SEGMENT_MIN_SIZE || splitSize > ZIP_SEGMENT_MAX_SIZE) {
throw new IllegalArgumentException("zip split segment size should between 64K and 4,294,967,295");
}

this.zipFile = zipFile;
this.splitSize = splitSize;

this.outputStream = Files.newOutputStream(zipFile.toPath());
this.outputStream = Files.newOutputStream(zipFile);
// write the zip split signature 0x08074B50 to the zip file
writeZipSplitSignature();
}

/**
* Create a split zip. If the zip file is smaller than the split size,
* then there will only be one split zip, and its suffix is .zip,
* otherwise the split segments should be like .z01, .z02, ... .z(N-1), .zip
*
* @param zipFile the zip file to write to
* @param splitSize the split size
*/
public ZipSplitOutputStream(final File zipFile, final long splitSize) throws IllegalArgumentException, IOException {
this(zipFile.toPath(), splitSize);
}

/**
* Some data can not be written to different split segments, for example:
* <p>
Expand Down Expand Up @@ -149,12 +163,9 @@ private void finish() throws IOException {
throw new IOException("This archive has already been finished");
}

final String zipFileBaseName = FileNameUtils.getBaseName(zipFile.getName());
final File lastZipSplitSegmentFile = new File(zipFile.getParentFile(), zipFileBaseName + ".zip");
final String zipFileBaseName = FileNameUtils.getBaseName(zipFile.getFileName().toString());
outputStream.close();
if (!zipFile.renameTo(lastZipSplitSegmentFile)) {
throw new IOException("Failed to rename " + zipFile + " to " + lastZipSplitSegmentFile);
}
Files.move(zipFile, zipFile.resolveSibling(zipFileBaseName + ".zip"), StandardCopyOption.ATOMIC_MOVE);
finished = true;
}

Expand All @@ -164,19 +175,17 @@ private void finish() throws IOException {
* @throws IOException
*/
private void openNewSplitSegment() throws IOException {
File newFile;
Path newFile;
if (currentSplitSegmentIndex == 0) {
outputStream.close();
newFile = createNewSplitSegmentFile(1);
if (!zipFile.renameTo(newFile)) {
throw new IOException("Failed to rename " + zipFile + " to " + newFile);
}
Files.move(zipFile, newFile, StandardCopyOption.ATOMIC_MOVE);
}

newFile = createNewSplitSegmentFile(null);

outputStream.close();
outputStream = Files.newOutputStream(newFile.toPath());
outputStream = Files.newOutputStream(newFile);
currentSplitSegmentBytesWritten = 0;
zipFile = newFile;
currentSplitSegmentIndex++;
Expand Down Expand Up @@ -215,19 +224,21 @@ private void writeZipSplitSignature() throws IOException {
* @return
* @throws IOException
*/
private File createNewSplitSegmentFile(final Integer zipSplitSegmentSuffixIndex) throws IOException {
final int newZipSplitSegmentSuffixIndex = zipSplitSegmentSuffixIndex == null ? (currentSplitSegmentIndex + 2) : zipSplitSegmentSuffixIndex;
final String baseName = FileNameUtils.getBaseName(zipFile.getName());
private Path createNewSplitSegmentFile(final Integer zipSplitSegmentSuffixIndex) throws IOException {
final int newZipSplitSegmentSuffixIndex = zipSplitSegmentSuffixIndex == null ? (currentSplitSegmentIndex + 2)
: zipSplitSegmentSuffixIndex;
final String baseName = FileNameUtils.getBaseNameFrom(zipFile);
String extension = ".z";
if (newZipSplitSegmentSuffixIndex <= 9) {
extension += "0" + newZipSplitSegmentSuffixIndex;
} else {
extension += newZipSplitSegmentSuffixIndex;
}

final File newFile = new File(zipFile.getParent(), baseName + extension);
String dir = Objects.nonNull(zipFile.getParent()) ? zipFile.getParent().toAbsolutePath().toString() : ".";
final Path newFile = zipFile.getFileSystem().getPath(dir, baseName + extension);

if (newFile.exists()) {
if (Files.exists(newFile)) {
throw new IOException("split zip segment " + baseName + extension + " already exists");
}
return newFile;
Expand Down

0 comments on commit 3adeef0

Please sign in to comment.