package com.door43.util;
import android.support.annotation.Nullable;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* This class handles zipping and un-zipping files and directories
*/
public class Zip {
/**
* Creates a zip archive
* http://stackoverflow.com/questions/6683600/zip-compress-a-folder-full-of-files-on-android
* @param sourcePath
* @param destPath
* @throws java.io.IOException
*/
public static void zip(String sourcePath, String destPath) throws IOException {
final int BUFFER = 2048;
File sourceFile = new File(sourcePath);
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(destPath);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
if (sourceFile.isDirectory()) {
// TRICKY: we add 1 to the base path length to exclude the leading path separator
zipSubFolder(out, sourceFile, sourceFile.getParent().length() + 1);
} else {
byte data[] = new byte[BUFFER];
FileInputStream fi = new FileInputStream(sourcePath);
origin = new BufferedInputStream(fi, BUFFER);
String[] segments = sourcePath.split("/");
String lastPathComponent = segments[segments.length - 1];
ZipEntry entry = new ZipEntry(lastPathComponent);
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
}
out.close();
}
/**
* Adds a file to a zip archive
* http://stackoverflow.com/questions/3048669/how-can-i-add-entries-to-an-existing-zip-file-in-java
* @param zipFile
* @param files
* @throws IOException
*/
@Deprecated
public static void addFilesToExistingZip(File zipFile, File[] files) throws IOException {
// get a temp file
File tempFile = File.createTempFile(zipFile.getName(), null);
// delete it, otherwise you cannot rename your existing zip to it.
tempFile.delete();
boolean renameOk=zipFile.renameTo(tempFile);
if (!renameOk)
{
throw new RuntimeException("could not rename the file "+zipFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath());
}
byte[] buf = new byte[1024];
ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));
ZipEntry entry = zin.getNextEntry();
while (entry != null) {
String name = entry.getName();
boolean notInFiles = true;
for (File f : files) {
if (f.getName().equals(name)) {
notInFiles = false;
break;
}
}
if (notInFiles) {
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(name));
// Transfer bytes from the ZIP file to the output file
int len;
while ((len = zin.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
entry = zin.getNextEntry();
}
// Close the streams
zin.close();
// Compress the files
for (int i = 0; i < files.length; i++) {
InputStream in = new FileInputStream(files[i]);
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(files[i].getName()));
// Transfer bytes from the file to the ZIP file
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
// Complete the entry
out.closeEntry();
in.close();
}
// Complete the ZIP file
out.close();
tempFile.delete();
}
/**
* Zips up a list of files to file
* @param files
* @param archivePath - destination file
*/
public static void zip(File[] files, File archivePath) throws IOException {
FileOutputStream dest = new FileOutputStream(archivePath);
zipToStream(files, dest);
}
/**
* Zips up a list of files to output stream
* @param files
* @param dest - destination output stream
*/
public static void zipToStream(File[] files, OutputStream dest) throws IOException {
final int BUFFER = 2048;
BufferedInputStream origin = null;
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
dest));
for(File f:files) {
if (f.isDirectory()) {
// TRICKY: we add 1 to the base path length to exclude the leading path separator
zipSubFolder(out, f, f.getParent().length() + 1);
} else {
byte data[] = new byte[BUFFER];
FileInputStream fi = new FileInputStream(f);
origin = new BufferedInputStream(fi, BUFFER);
String[] segments = f.getAbsolutePath().split("/");
String lastPathComponent = segments[segments.length - 1];
ZipEntry entry = new ZipEntry(lastPathComponent);
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
}
}
out.close();
}
/**
* Zips up a list of files that are mapped to a relative directory in the resulting archive.
* TODO: we'd like to begin using this so we don't have to organize everything into a directory before zipping
* @param files
* @param archivePath
* @throws IOException
*/
public static void zip(Map<File, String> files, File archivePath) throws IOException {
final int BUFFER = 2048;
BufferedInputStream origin = null;
FileOutputStream dest = new FileOutputStream(archivePath);
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(dest));
Iterator it = files.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<File, String> pair = (Map.Entry)it.next();
// clean the target path
pair.setValue(pair.getValue().replaceAll("^/+", "").replace("/*$", "") + "/");
if (pair.getKey().isDirectory()) {
// TRICKY: we add 1 to the base path length to exclude the leading path separator
zipSubFolder(out, pair.getKey(), pair.getValue() + pair.getKey().getName());
} else {
byte data[] = new byte[BUFFER];
FileInputStream fi = new FileInputStream(pair.getKey());
origin = new BufferedInputStream(fi, BUFFER);
String relativePath = pair.getValue() + pair.getKey().getName();
ZipEntry entry = new ZipEntry(pair.getValue());
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
}
}
out.close();
}
/**
* Zips up a sub folder
* @param out
* @param folder
* @param basePathLength
* @throws IOException
*/
private static void zipSubFolder(ZipOutputStream out, File folder, int basePathLength) throws IOException {
final int BUFFER = 2048;
File[] fileList = folder.listFiles();
BufferedInputStream origin = null;
for (File file : fileList) {
if (file.isDirectory()) {
zipSubFolder(out, file, basePathLength);
} else {
byte data[] = new byte[BUFFER];
String unmodifiedFilePath = file.getPath();
String relativePath = unmodifiedFilePath.substring(basePathLength);
FileInputStream fi = new FileInputStream(unmodifiedFilePath);
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(relativePath);
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
}
}
/**
* Zips up a sub folder
* @param out
* @param folder
* @param relativePath
* @throws IOException
*/
private static void zipSubFolder(ZipOutputStream out, File folder, String relativePath) throws IOException {
final int BUFFER = 2048;
File[] fileList = folder.listFiles();
BufferedInputStream origin = null;
for (File file : fileList) {
if (file.isDirectory()) {
zipSubFolder(out, file, relativePath + "/" + file.getName());
} else {
byte data[] = new byte[BUFFER];
FileInputStream fi = new FileInputStream(file.getPath());
origin = new BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(relativePath + "/" + file.getName());
out.putNextEntry(entry);
int count;
while ((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
}
}
/**
* Extracts a zip archive
* @param zipPath
* @throws IOException
*/
public static void unzip(String zipPath, String destPath) throws IOException {
InputStream is;
ZipInputStream zis;
String filename;
ZipEntry ze;
int count;
byte[] buffer = new byte[1024];
is = new FileInputStream(zipPath);
zis = new ZipInputStream(new BufferedInputStream(is));
File destDir = new File(destPath);
destDir.mkdirs();
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
File f = new File(destPath, filename);
if (ze.isDirectory()) {
f.mkdirs();
continue;
}
f.getParentFile().mkdirs();
f.createNewFile();
FileOutputStream fout = new FileOutputStream(f.getAbsolutePath());
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
}
/**
* Extracts a zip archive
* @param zipArchive
* @param destDir - place to store unzipped file
* @throws IOException
*/
public static void unzip(File zipArchive, File destDir) throws IOException {
InputStream is;
ZipInputStream zis;
String filename;
ZipEntry ze;
int count;
is = new FileInputStream(zipArchive);
unzipFromStream(is, destDir);
}
/**
* Extracts a zip archive from a stream
* @param is - input stream of zip file
* @param destDir - place to store unzipped file
* @throws IOException
*/
public static void unzipFromStream(InputStream is, File destDir) throws IOException {
byte[] buffer = new byte[1024];
ZipInputStream zis;
ZipEntry ze;
String filename;
int count;
zis = new ZipInputStream(new BufferedInputStream(is));
destDir.mkdirs();
while ((ze = zis.getNextEntry()) != null) {
filename = ze.getName();
File f = new File(destDir, filename);
if (ze.isDirectory()) {
f.mkdirs();
continue;
}
f.getParentFile().mkdirs();
f.createNewFile();
FileOutputStream fout = new FileOutputStream(f.getAbsolutePath());
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
}
/**
* Lists the contents of the zip file
* @param zipArchive
* @return
* @throws IOException
*/
public static String[] list(File zipArchive) throws IOException {
InputStream is;
ZipInputStream zis;
ZipEntry ze;
is = new FileInputStream(zipArchive);
zis = new ZipInputStream(new BufferedInputStream(is));
List<String> files = new ArrayList<>();
while ((ze = zis.getNextEntry()) != null) {
files.add(ze.getName());
if (ze.isDirectory()) {
continue;
}
zis.closeEntry();
}
zis.close();
return files.toArray(new String[files.size()]);
}
/**
* Reads the contents of a file from the zip archive
* @param zipArchive
* @param path
* @return
*/
public static String read(File zipArchive, String path) throws IOException {
InputStream is;
is = new FileInputStream(zipArchive);
return readInputStream(is, path);
}
/**
* Reads the contents of a file from the zip archive
* @param zipStream
* @param path
* @return
*/ @Nullable
public static String readInputStream(InputStream zipStream, String path) throws IOException {
String contents = null;
ZipInputStream zis;
ZipEntry ze;
zis = new ZipInputStream(new BufferedInputStream(zipStream));
while ((ze = zis.getNextEntry()) != null) {
if (ze.isDirectory()) {
continue;
}
if(ze.getName().equalsIgnoreCase(path)) {
BufferedReader reader = new BufferedReader(new InputStreamReader(zis));
StringBuilder sb = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
sb.append(line).append('\n');
}
contents = sb.toString();
}
zis.closeEntry();
if(contents != null) {
break;
}
}
zis.close();
return contents;
}
}