package com.faforever.client.io;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.invoke.MethodHandles;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import static java.nio.file.StandardOpenOption.CREATE;
public final class Unzipper {
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
private final ZipInputStream zipInputStream;
private ByteCountListener byteCountListener;
private int byteCountInterval;
private int bufferSize;
private long totalBytes;
private Path targetDirectory;
private long lastCountUpdate;
private Unzipper(ZipInputStream zipInputStream) {
this.zipInputStream = zipInputStream;
// 4K
bufferSize = 0x1000;
byteCountInterval = 40;
}
public static Unzipper from(ZipInputStream zipInputStream) {
return new Unzipper(zipInputStream);
}
public Unzipper to(Path targetDirectory) {
this.targetDirectory = targetDirectory;
return this;
}
public Unzipper byteCountInterval(int byteCountInterval) {
this.byteCountInterval = byteCountInterval;
return this;
}
public Unzipper listener(ByteCountListener byteCountListener) {
this.byteCountListener = byteCountListener;
return this;
}
public Unzipper bufferSize(int bufferSize) {
this.bufferSize = bufferSize;
return this;
}
public Unzipper totalBytes(long totalBytes) {
this.totalBytes = totalBytes;
return this;
}
public void unzip() throws IOException {
byte[] buffer = new byte[bufferSize];
long bytesDone = 0;
ZipEntry zipEntry;
while ((zipEntry = zipInputStream.getNextEntry()) != null) {
Path targetFile = targetDirectory.resolve(zipEntry.getName());
if (zipEntry.isDirectory()) {
logger.trace("Creating directory {}", targetFile);
Files.createDirectories(targetFile);
continue;
}
Path parentDirectory = targetFile.getParent();
if (Files.notExists(parentDirectory)) {
logger.trace("Creating directory {}", parentDirectory);
Files.createDirectories(parentDirectory);
}
long compressedSize = zipEntry.getCompressedSize();
if (compressedSize != -1) {
bytesDone += compressedSize;
}
logger.trace("Writing file {}", targetFile);
try (OutputStream outputStream = Files.newOutputStream(targetFile, CREATE)) {
int length;
while ((length = zipInputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, length);
long now = System.currentTimeMillis();
if (byteCountListener != null && lastCountUpdate < now - byteCountInterval) {
byteCountListener.updateBytesWritten(bytesDone, totalBytes);
lastCountUpdate = now;
}
}
}
}
}
}