How to unzip files programmatically in Android? - java

I need a small code snippet which unzips a few files from a given .zip file and gives the separate files according to the format they were in the zipped file. Please post your knowledge and help me out.

Had peno's version optimised a bit. The increase in performance is perceptible.
private boolean unpackZip(String path, String zipname)
{
InputStream is;
ZipInputStream zis;
try
{
String filename;
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null)
{
filename = ze.getName();
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
File fmd = new File(path + filename);
fmd.mkdirs();
continue;
}
FileOutputStream fout = new FileOutputStream(path + filename);
while ((count = zis.read(buffer)) != -1)
{
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}

Based on Vasily Sochinsky's answer a bit tweaked & with a small fix:
public static void unzip(File zipFile, File targetDirectory) throws IOException {
ZipInputStream zis = new ZipInputStream(
new BufferedInputStream(new FileInputStream(zipFile)));
try {
ZipEntry ze;
int count;
byte[] buffer = new byte[8192];
while ((ze = zis.getNextEntry()) != null) {
File file = new File(targetDirectory, ze.getName());
File dir = ze.isDirectory() ? file : file.getParentFile();
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Failed to ensure directory: " +
dir.getAbsolutePath());
if (ze.isDirectory())
continue;
FileOutputStream fout = new FileOutputStream(file);
try {
while ((count = zis.read(buffer)) != -1)
fout.write(buffer, 0, count);
} finally {
fout.close();
}
/* if time should be restored as well
long time = ze.getTime();
if (time > 0)
file.setLastModified(time);
*/
}
} finally {
zis.close();
}
}
Notable differences
public static - this is a static utility method that can be anywhere.
2 File parameters because String are :/ for files and one could not specify where the zip file is to be extracted before. Also path + filename concatenation > https://stackoverflow.com/a/412495/995891
throws - because catch late - add a try catch if really not interested in them.
actually makes sure that the required directories exist in all cases. Not every zip contains all the required directory entries in advance of file entries. This had 2 potential bugs:
if the zip contains an empty directory and instead of the resulting directory there is an existing file, this was ignored. The return value of mkdirs() is important.
could crash on zip files that don't contain directories.
increased write buffer size, this should improve performance a bit. Storage is usually in 4k blocks and writing in smaller chunks is usually slower than necessary.
uses the magic of finally to prevent resource leaks.
So
unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));
should do the equivalent of the original
unpackZip("/sdcard/", "pictures.zip")

This is my unzip method, which I use:
private boolean unpackZip(String path, String zipname)
{
InputStream is;
ZipInputStream zis;
try
{
is = new FileInputStream(path + zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
while((ze = zis.getNextEntry()) != null)
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
String filename = ze.getName();
FileOutputStream fout = new FileOutputStream(path + filename);
// reading and writing
while((count = zis.read(buffer)) != -1)
{
baos.write(buffer, 0, count);
byte[] bytes = baos.toByteArray();
fout.write(bytes);
baos.reset();
}
fout.close();
zis.closeEntry();
}
zis.close();
}
catch(IOException e)
{
e.printStackTrace();
return false;
}
return true;
}

The Kotlin way
//FileExt.kt
data class ZipIO (val entry: ZipEntry, val output: File)
fun File.unzip(unzipLocationRoot: File? = null) {
val rootFolder = unzipLocationRoot ?: File(parentFile.absolutePath + File.separator + nameWithoutExtension)
if (!rootFolder.exists()) {
rootFolder.mkdirs()
}
ZipFile(this).use { zip ->
zip
.entries()
.asSequence()
.map {
val outputFile = File(rootFolder.absolutePath + File.separator + it.name)
ZipIO(it, outputFile)
}
.map {
it.output.parentFile?.run{
if (!exists()) mkdirs()
}
it
}
.filter { !it.entry.isDirectory }
.forEach { (entry, output) ->
zip.getInputStream(entry).use { input ->
output.outputStream().use { output ->
input.copyTo(output)
}
}
}
}
}
Usage
val zipFile = File("path_to_your_zip_file")
file.unzip()

Android has build-in Java API. Check out java.util.zip package.
The class ZipInputStream is what you should look into. Read ZipEntry from the ZipInputStream and dump it into filesystem/folder. Check similar example to compress into zip file.

use the following class
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import android.util.Log;
public class DecompressFast {
private String _zipFile;
private String _location;
public DecompressFast(String zipFile, String location) {
_zipFile = zipFile;
_location = location;
_dirChecker("");
}
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
ZipEntry ze = null;
while ((ze = zin.getNextEntry()) != null) {
Log.v("Decompress", "Unzipping " + ze.getName());
if(ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_location + ze.getName());
BufferedOutputStream bufout = new BufferedOutputStream(fout);
byte[] buffer = new byte[1024];
int read = 0;
while ((read = zin.read(buffer)) != -1) {
bufout.write(buffer, 0, read);
}
bufout.close();
zin.closeEntry();
fout.close();
}
}
zin.close();
Log.d("Unzip", "Unzipping complete. path : " +_location );
} catch(Exception e) {
Log.e("Decompress", "unzip", e);
Log.d("Unzip", "Unzipping failed");
}
}
private void _dirChecker(String dir) {
File f = new File(_location + dir);
if(!f.isDirectory()) {
f.mkdirs();
}
}
}
How to use
String zipFile = Environment.getExternalStorageDirectory() + "/the_raven.zip"; //your zip file location
String unzipLocation = Environment.getExternalStorageDirectory() + "/unzippedtestNew/"; // destination folder location
DecompressFast df= new DecompressFast(zipFile, unzipLocation);
df.unzip();
Permissions
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

While the answers that are already here work well, I found that they were slightly slower than I had hoped for. Instead I used zip4j, which I think is the best solution because of its speed. It also allowed for different options for the amount of compression, which I found useful.

According to #zapl answer,Unzip with progress report:
public interface UnzipFile_Progress
{
void Progress(int percent, String FileName);
}
// unzip(new File("/sdcard/pictures.zip"), new File("/sdcard"));
public static void UnzipFile(File zipFile, File targetDirectory, UnzipFile_Progress progress) throws IOException,
FileNotFoundException
{
long total_len = zipFile.length();
long total_installed_len = 0;
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(new FileInputStream(zipFile)));
try
{
ZipEntry ze;
int count;
byte[] buffer = new byte[1024];
while ((ze = zis.getNextEntry()) != null)
{
if (progress != null)
{
total_installed_len += ze.getCompressedSize();
String file_name = ze.getName();
int percent = (int)(total_installed_len * 100 / total_len);
progress.Progress(percent, file_name);
}
File file = new File(targetDirectory, ze.getName());
File dir = ze.isDirectory() ? file : file.getParentFile();
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
if (ze.isDirectory())
continue;
FileOutputStream fout = new FileOutputStream(file);
try
{
while ((count = zis.read(buffer)) != -1)
fout.write(buffer, 0, count);
} finally
{
fout.close();
}
// if time should be restored as well
long time = ze.getTime();
if (time > 0)
file.setLastModified(time);
}
} finally
{
zis.close();
}
}

public class MainActivity extends Activity {
private String LOG_TAG = MainActivity.class.getSimpleName();
private File zipFile;
private File destination;
private TextView status;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
status = (TextView) findViewById(R.id.main_status);
status.setGravity(Gravity.CENTER);
if ( initialize() ) {
zipFile = new File(destination, "BlueBoxnew.zip");
try {
Unzipper.unzip(zipFile, destination);
status.setText("Extracted to \n"+destination.getAbsolutePath());
} catch (ZipException e) {
Log.e(LOG_TAG, e.getMessage());
} catch (IOException e) {
Log.e(LOG_TAG, e.getMessage());
}
} else {
status.setText("Unable to initialize sd card.");
}
}
public boolean initialize() {
boolean result = false;
File sdCard = new File(Environment.getExternalStorageDirectory()+"/zip/");
//File sdCard = Environment.getExternalStorageDirectory();
if ( sdCard != null ) {
destination = sdCard;
if ( !destination.exists() ) {
if ( destination.mkdir() ) {
result = true;
}
} else {
result = true;
}
}
return result;
}
}
->Helper Class(Unzipper.java)
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipInputStream;
import android.util.Log;
public class Unzipper {
private static String LOG_TAG = Unzipper.class.getSimpleName();
public static void unzip(final File file, final File destination) throws ZipException, IOException {
new Thread() {
public void run() {
long START_TIME = System.currentTimeMillis();
long FINISH_TIME = 0;
long ELAPSED_TIME = 0;
try {
ZipInputStream zin = new ZipInputStream(new FileInputStream(file));
String workingDir = destination.getAbsolutePath()+"/";
byte buffer[] = new byte[4096];
int bytesRead;
ZipEntry entry = null;
while ((entry = zin.getNextEntry()) != null) {
if (entry.isDirectory()) {
File dir = new File(workingDir, entry.getName());
if (!dir.exists()) {
dir.mkdir();
}
Log.i(LOG_TAG, "[DIR] "+entry.getName());
} else {
FileOutputStream fos = new FileOutputStream(workingDir + entry.getName());
while ((bytesRead = zin.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
fos.close();
Log.i(LOG_TAG, "[FILE] "+entry.getName());
}
}
zin.close();
FINISH_TIME = System.currentTimeMillis();
ELAPSED_TIME = FINISH_TIME - START_TIME;
Log.i(LOG_TAG, "COMPLETED in "+(ELAPSED_TIME/1000)+" seconds.");
} catch (Exception e) {
Log.e(LOG_TAG, "FAILED");
}
};
}.start();
}
}
->xml layout(activity_main.xml):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<TextView
android:id="#+id/main_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="#string/hello_world" />
</RelativeLayout>
->permission in Menifest file:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Here is a ZipFileIterator (like a java Iterator, but for zip files):
package ch.epfl.bbp.io;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipFileIterator implements Iterator<File> {
private byte[] buffer = new byte[1024];
private FileInputStream is;
private ZipInputStream zis;
private ZipEntry ze;
public ZipFileIterator(File file) throws FileNotFoundException {
is = new FileInputStream(file);
zis = new ZipInputStream(new BufferedInputStream(is));
}
#Override
public boolean hasNext() {
try {
return (ze = zis.getNextEntry()) != null;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
#Override
public File next() {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int count;
String filename = ze.getName();
File tmpFile = File.createTempFile(filename, "tmp");
tmpFile.deleteOnExit();// TODO make it configurable
FileOutputStream fout = new FileOutputStream(tmpFile);
while ((count = zis.read(buffer)) != -1) {
baos.write(buffer, 0, count);
byte[] bytes = baos.toByteArray();
fout.write(bytes);
baos.reset();
}
fout.close();
zis.closeEntry();
return tmpFile;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override
public void remove() {
throw new RuntimeException("not implemented");
}
public void close() {
try {
zis.close();
is.close();
} catch (IOException e) {// nope
}
}
}

Minimal example I used to unzip a specific file from my zipfile into my applications cache folder. I then read the manifest file using a different method.
private void unzipUpdateToCache() {
ZipInputStream zipIs = new ZipInputStream(context.getResources().openRawResource(R.raw.update));
ZipEntry ze = null;
try {
while ((ze = zipIs.getNextEntry()) != null) {
if (ze.getName().equals("update/manifest.json")) {
FileOutputStream fout = new FileOutputStream(context.getCacheDir().getAbsolutePath() + "/manifest.json");
byte[] buffer = new byte[1024];
int length = 0;
while ((length = zipIs.read(buffer))>0) {
fout.write(buffer, 0, length);
}
zipIs .closeEntry();
fout.close();
}
}
zipIs .close();
} catch (IOException e) {
e.printStackTrace();
}
}

I'm working with zip files which Java's ZipFile class isn't able to handle. Java 8 apparently can't handle compression method 12 (bzip2 I believe). After trying a number of methods including zip4j (which also fails with these particular files due to another issue), I had success with Apache's commons-compress which supports additional compression methods as mentioned here.
Note that the ZipFile class below is not the one from java.util.zip.
It's actually org.apache.commons.compress.archivers.zip.ZipFile so be careful with the imports.
try (ZipFile zipFile = new ZipFile(archiveFile)) {
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
ZipArchiveEntry entry = entries.nextElement();
File entryDestination = new File(destination, entry.getName());
if (entry.isDirectory()) {
entryDestination.mkdirs();
} else {
entryDestination.getParentFile().mkdirs();
try (InputStream in = zipFile.getInputStream(entry); OutputStream out = new FileOutputStream(entryDestination)) {
IOUtils.copy(in, out);
}
}
}
} catch (IOException ex) {
log.debug("Error unzipping archive file: " + archiveFile, ex);
}
For Gradle:
compile 'org.apache.commons:commons-compress:1.18'

Based on zapl's answer, adding try() around Closeable's closes the streams automatically after use.
public static void unzip(File zipFile, File targetDirectory) {
try (FileInputStream fis = new FileInputStream(zipFile)) {
try (BufferedInputStream bis = new BufferedInputStream(fis)) {
try (ZipInputStream zis = new ZipInputStream(bis)) {
ZipEntry ze;
int count;
byte[] buffer = new byte[Constant.DefaultBufferSize];
while ((ze = zis.getNextEntry()) != null) {
File file = new File(targetDirectory, ze.getName());
File dir = ze.isDirectory() ? file : file.getParentFile();
if (!dir.isDirectory() && !dir.mkdirs())
throw new FileNotFoundException("Failed to ensure directory: " + dir.getAbsolutePath());
if (ze.isDirectory())
continue;
try (FileOutputStream fout = new FileOutputStream(file)) {
while ((count = zis.read(buffer)) != -1)
fout.write(buffer, 0, count);
}
}
}
}
} catch (Exception ex) {
//handle exception
}
}
Using Constant.DefaultBufferSize (65536) gotten from C# .NET 4 Stream.CopyTo from Jon Skeet's answer here:
https://stackoverflow.com/a/411605/1876355
I always just see posts using byte[1024] or byte[4096] buffer, never knew it can be much larger which improves performance and is still working perfectly normal.
Here is the Stream Source code:
https://referencesource.microsoft.com/#mscorlib/system/io/stream.cs
//We pick a value that is the largest multiple of 4096 that is still smaller than the large object heap threshold (85K).
// The CopyTo/CopyToAsync buffer is short-lived and is likely to be collected at Gen0, and it offers a significant
// improvement in Copy performance.
private const int _DefaultCopyBufferSize = 81920;
However, I dialed it back to 65536 which is also a multiple of 4096 just to be safe.

Password Protected Zip File
if you want to compress files with password you can take a look at this library that can zip files with password easily:
Zip:
ZipArchive zipArchive = new ZipArchive();
zipArchive.zip(targetPath,destinationPath,password);
Unzip:
ZipArchive zipArchive = new ZipArchive();
zipArchive.unzip(targetPath,destinationPath,password);
Rar:
RarArchive rarArchive = new RarArchive();
rarArchive.extractArchive(file archive, file destination);
The documentation of this library is good enough, I just added a few examples from there.
It's totally free and wrote specially for android.

Here is more concise version of #arsent solution:
fun File.unzip(to: File? = null) {
val destinationDir = to ?: File(parentFile, nameWithoutExtension)
destinationDir.mkdirs()
ZipFile(this).use { zipFile ->
zipFile
.entries()
.asSequence()
.filter { !it.isDirectory }
.forEach { zipEntry ->
val currFile = File(destinationDir, zipEntry.name)
currFile.parentFile?.mkdirs()
zipFile.getInputStream(zipEntry).use { input ->
currFile.outputStream().use { output -> input.copyTo(output) }
}
}
}
}

Related

How to implement Parallel Zip Creation with ScatterZipOutputStream with Zip64 Support?

I am wondering if any one can help implementing Parallel Zip Creation using ScatterZipOutputStream . I have searched a lot but no where I am finding an example for the same.
https://commons.apache.org/proper/commons-compress/zip.html
I have tried making Zip, Zipping a directory etc with ZipArchiveOutputStream . Now, I am trying to do that in parallel.
public static void makeZip(String filename) throws IOException,
ArchiveException {
File sourceFile = new File(filename);
final OutputStream out = new FileOutputStream(filename.substring(0, filename.lastIndexOf('.')) + ".zip");
ZipArchiveOutputStream os = new ZipArchiveOutputStream(out);
os.setUseZip64(Zip64Mode.AsNeeded);
os.putArchiveEntry(new ZipArchiveEntry(sourceFile.getName()));
IOUtils.copy(new FileInputStream(sourceFile), os);
os.closeArchiveEntry();
os.close();
}
It should be able to process individual files as thread and then combine it to write the result zip.
Following is the working code of both zip and unzip:
1. Change path for sourceFolder and zipFilePath
2. Zipping only *.text type of files it can be any type or all the files
3. Unzipped files at sourceFolder/unzip/
Import following dependency in build.gradle or in pom.xml
implementation("org.apache.commons:commons-compress:1.18")
implementation("commons-io:commons-io:2.6")
Ref: https://mvnrepository.com/artifact/org.apache.commons/commons-compress/1.18
https://mvnrepository.com/artifact/commons-io/commons-io/2.6
//code
import org.apache.commons.compress.archivers.zip.*;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class ZipMain {
static ParallelScatterZipCreator scatterZipCreator = new ParallelScatterZipCreator();
static ScatterZipOutputStream dirs;
static {
try {
dirs = ScatterZipOutputStream.fileBased(File.createTempFile("java-zip-dirs", "tmp"));
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws IOException {
String sourceFolder = "/Users/<user>/Desktop/";
String zipFilePath = "/Users/<user>/Desktop/Desk.zip";
String fileTypesToBeAddedToZip = "txt";
zip(sourceFolder, zipFilePath, fileTypesToBeAddedToZip);
unzip(zipFilePath, sourceFolder + "/unzip/");
}
private static void zip(String sourceFolder, String zipFilePath, String fileTypesToBeAddedToZip) throws IOException {
OutputStream outputStream = null;
ZipArchiveOutputStream zipArchiveOutputStream = null;
try {
File srcFolder = new File(sourceFolder);
if (srcFolder.isDirectory()) {
// uncomment following code if you want to add all files under srcFolder
//Iterator<File> fileIterator = Arrays.asList(srcFolder.listFiles()).iterator();
Iterator<File> fileIterator = FileUtils.iterateFiles(srcFolder, new String[]{fileTypesToBeAddedToZip}, true);
File zipFile = new File(zipFilePath);
zipFile.delete();
outputStream = new FileOutputStream(zipFile);
zipArchiveOutputStream = new ZipArchiveOutputStream(outputStream);
zipArchiveOutputStream.setUseZip64(Zip64Mode.AsNeeded);
int srcFolderLength = srcFolder.getAbsolutePath().length() + 1; // +1 to remove the last file separator
while (fileIterator.hasNext()) {
File file = fileIterator.next();
// uncomment following code if you want to add all files under srcFolder
//if (file.isDirectory()) {
// continue;
// }
String relativePath = file.getAbsolutePath().substring(srcFolderLength);
InputStreamSupplier streamSupplier = () -> {
InputStream is = null;
try {
is = Files.newInputStream(file.toPath());
} catch (IOException e) {
e.printStackTrace();
}
return is;
};
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(relativePath);
zipArchiveEntry.setMethod(ZipEntry.DEFLATED);
scatterZipCreator.addArchiveEntry(zipArchiveEntry, streamSupplier);
}
scatterZipCreator.writeTo(zipArchiveOutputStream);
}
if (zipArchiveOutputStream != null) {
zipArchiveOutputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
outputStream.close();
}
}
}
private static void unzip(String zipFilePath, String destDir) {
File dir = new File(destDir);
// create output directory if it doesn't exist
if (!dir.exists()) {
dir.mkdirs();
} else {
dir.delete();
}
FileInputStream fis;
//buffer for read and write data to file
byte[] buffer = new byte[1024];
try {
fis = new FileInputStream(zipFilePath);
ZipInputStream zis = new ZipInputStream(fis);
ZipEntry ze = zis.getNextEntry();
while (ze != null) {
String fileName = ze.getName();
File newFile = new File(destDir + File.separator + fileName);
System.out.println("Unzipping to " + newFile.getAbsolutePath());
//create directories for sub directories in zip
String parentFolder = newFile.getParent();
File folder = new File(parentFolder);
folder.mkdirs();
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
//close this ZipEntry
zis.closeEntry();
ze = zis.getNextEntry();
}
//close last ZipEntry
zis.closeEntry();
zis.close();
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Ref: Fast zipping folder using java ParallelScatterZipCreator

Java Basics - Looping through folder

I am very new to java and coming from a js background. I am attempting to loop through a folder full of files and zipping it. Currently, I have done the zipping part successfully, but doing by statically adding the files. The answer is obviously a loop from a programming perspective. I am having trouble looping a list and making it equal to the zipping method below. Online Resources are not making sense much sense to me due to my beginner skill.
package zipFile;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipFiles {
public static void main(String[] args) {
try {
FileOutputStream fos = new FileOutputStream("atest.zip");
ZipOutputStream zos = new ZipOutputStream(fos);
String file1Name = "src/resources/text1";
String file2Name = "src/resources/text2";
String file3Name = "src/resources/text3";
String file4Name = "src/resources/text4";
String file5Name = "src/resources/text5";
String file6Name = "src/resources/text6";
addToZipFile(file1Name, zos);
addToZipFile(file2Name, zos);
addToZipFile(file3Name, zos);
addToZipFile(file4Name, zos);
addToZipFile(file5Name, zos);
addToZipFile(file6Name, zos);
zos.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void addToZipFile(String fileName, ZipOutputStream zos) throws FileNotFoundException, IOException {
System.out.println("Writing '" + fileName + "' to zip file");
File file = new File(fileName);
FileInputStream fis = new FileInputStream(file);
ZipEntry zipEntry = new ZipEntry(fileName);
zos.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zos.write(bytes, 0, length);
}
zos.closeEntry();
fis.close();
}
}
The answer is in this article: http://www.baeldung.com/java-compress-and-uncompress
This code zips multiple files (Very similar to your code but slightly changed):
public class ZipMultipleFiles {
public static void main(String[] args) throws IOException {
List<String> srcFiles = Arrays.asList("test1.txt", "test2.txt");
FileOutputStream fos = new FileOutputStream("multiCompressed.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
for (String srcFile : srcFiles) {
File fileToZip = new File(srcFile);
FileInputStream fis = new FileInputStream(fileToZip);
ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
fis.close();
}
zipOut.close();
fos.close();
}
}
EDIT:
This line in the code creates an array that is easy to go through in a while loop:
List<String> srcFiles = Arrays.asList("test1.txt", "test2.txt");
basically used finding children of a folder method thanks to Elliotk link. I am making the string equal to the path of the parent folder - >checking if whether if its a directory - > list its files -> get the names and while loop to write all of them to a zipped folder
here is my whole code
package zipfolder2;
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class zipfolders2 {
public static void main(String[] args) {
try {
String sourceFile = "src/resources";
FileOutputStream fos = new FileOutputStream("zippedfiles.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
File fileToZip = new File(sourceFile);
zipFile(fileToZip, fileToZip.getName(), zipOut);
zipOut.close();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
if (fileToZip.isHidden()) {
return;
}
if (fileToZip.isDirectory()) {
File[] children = fileToZip.listFiles();
for (File childFile : children) {
zipFile(childFile, fileName + "/" + childFile.getName(), zipOut);
}
return;
}
FileInputStream fis = new FileInputStream(fileToZip);
ZipEntry zipEntry = new ZipEntry(fileName);
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
fis.close();
}
}

Unzip to folder in internal storage

I'm trying to create a folder in internal storage called "unzip" and then unzip a file into the internal storage "unzip folder". Not sure what i'm doing wrong? If you could please explain what's wrong it'd be great! Thanks
Edit: The issues is that I don't think the folder is being created and the file is also not being unzipped.
public void send(View view) {
try {
File mydir = this.getDir("unzip", Context.MODE_PRIVATE);//create folder in internal storage
unzip(getFilesDir().getAbsolutePath(), job_no, getFilesDir() + "/unzip/");
} catch (IOException e) {
}
}
public void unzip(String filepath, String filename, String unzip_path) throws IOException {
InputStream is = new FileInputStream(filepath + filename);
ZipInputStream zis = new ZipInputStream(new BufferedInputStream(is));
try {
ZipEntry ze;
while ((ze = zis.getNextEntry()) != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int count;
String filename_temp = ze.getName();
File fmd = new File(filepath + filename_temp);
if (!fmd.getParentFile().exists()) {
fmd.getParentFile().mkdirs();
}
FileOutputStream fout = new FileOutputStream(unzip_path + filename_temp);
while ((count = zis.read(buffer)) != -1) {
baos.write(buffer, 0, count);
byte[] bytes = baos.toByteArray();
fout.write(bytes);
baos.reset();
}
fout.close();
//}
}
} finally {
zis.close();
}
}
This will get absolute storage device.
final static String MEDIA_PATH = Environment.getExternalStorageDirectory().getPath() + "/";

Creating folder unzipping epub in Android

I have read a lot of threads on this site regarding my issue, and the code I have is based off some of them. The problem I have currently is that unzipping an ePub results in only the mimetype file being extracted with LogCat getting errors in creating a folder from what I understand.
Following is what I use to decompress any zip file, I got it from Problem when Unzipping:
/**
* #author jon
*/
public class Decompress {
private String _zipFile;
private String _location;
ZipEntry ze = null;
public Decompress(String zipFile, String location) {
_zipFile = zipFile;
_location = location;
_dirChecker("");
}
public void unzip() {
try {
FileInputStream fin = new FileInputStream(_zipFile);
ZipInputStream zin = new ZipInputStream(fin);
while ((ze = zin.getNextEntry()) != null) {
Log.v("Decompress", "Unzipping " + ze.getName());
if (ze.isDirectory()) {
_dirChecker(ze.getName());
} else {
FileOutputStream fout = new FileOutputStream(_location
+ ze.getName());
for (int c = zin.read(); c != -1; c = zin.read()) {
fout.write(c);
}
zin.closeEntry();
fout.close();
}
}
zin.close();
} catch (Exception e) {
Log.e("Decompress", "unzip", e);
}
}
private void _dirChecker(String dir) {
File f = new File(_location + dir);
Log.d("Decompress", f.getAbsolutePath().toString());
if (!f.isDirectory()) {
f.mkdirs();
}
}
}
Other articles I have referred to:
Unzip a zipped file on sd card in Android application
Why my decompress class don't make directories?
Uzip folders recursively -android
Render epub files in android
Android EPUBLIB read/load content
Here is the LogCat Error I receive on the second file(/folder) after the mimetype file.
03-24 10:38:13.991: E/Decompress(23190): java.io.FileNotFoundException: /storage/emulated/0/unzipped/yamani/CDS-suggestions.docx: open failed: ENOENT (No such file or directory)
I'm currently using a regular zip file to test out my code.
Thanks for the help.
Was able to solve my problem by using another snippet found on this site.
Source:
public class unpackZip {
public boolean unzip(String zipname, String path) {
InputStream is;
ZipInputStream zis;
try {
String filename;
is = new FileInputStream(zipname);
zis = new ZipInputStream(new BufferedInputStream(is));
ZipEntry ze;
byte[] buffer = new byte[1024];
int count;
while ((ze = zis.getNextEntry()) != null) {
// zapis do souboru
filename = ze.getName();
// Need to create directories if not exists, or
// it will generate an Exception...
if (ze.isDirectory()) {
File fmd = new File(path, filename);
fmd.mkdirs();
continue;
} else {
// Make this part of the code more efficient .code-revise
File fmd = new File(path, filename);
Log.d("Unzipping", fmd.getParentFile().getPath());
String parent = fmd.getParentFile().getPath();
File fmd_1 = new File(parent);
fmd_1.mkdirs();
// end of .code-revise
}
FileOutputStream fout = new FileOutputStream(path + filename);
// cteni zipu a zapis
while ((count = zis.read(buffer)) != -1) {
fout.write(buffer, 0, count);
}
fout.close();
zis.closeEntry();
}
zis.close();
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
}

Compress directory to tar.gz with Commons Compress

I'm running into a problem using the commons compress library to create a tar.gz of a directory. I have a directory structure that is as follows.
parent/
child/
file1.raw
fileN.raw
I'm using the following code to do the compression. It runs fine without exceptions. However, when I try to decompress that tar.gz, I get a single file with the name "childDirToCompress". Its the correct size so the files have clearly been appended to each other in the tarring process. The desired output would be a directory. I can't figure out what I'm doing wrong. Can any wise commons compresser set me upon the correct path?
CreateTarGZ() throws CompressorException, FileNotFoundException, ArchiveException, IOException {
File f = new File("parent");
File f2 = new File("parent/childDirToCompress");
File outFile = new File(f2.getAbsolutePath() + ".tar.gz");
if(!outFile.exists()){
outFile.createNewFile();
}
FileOutputStream fos = new FileOutputStream(outFile);
TarArchiveOutputStream taos = new TarArchiveOutputStream(new GZIPOutputStream(new BufferedOutputStream(fos)));
taos.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_STAR);
taos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
addFilesToCompression(taos, f2, ".");
taos.close();
}
private static void addFilesToCompression(TarArchiveOutputStream taos, File file, String dir) throws IOException{
taos.putArchiveEntry(new TarArchiveEntry(file, dir));
if (file.isFile()) {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
IOUtils.copy(bis, taos);
taos.closeArchiveEntry();
bis.close();
}
else if(file.isDirectory()) {
taos.closeArchiveEntry();
for (File childFile : file.listFiles()) {
addFilesToCompression(taos, childFile, file.getName());
}
}
}
I followed this solution and it worked until I was processing a larger set of files and it randomly crashes after processing 15000 - 16000 files. the following line is leaking file handlers:
IOUtils.copy(new FileInputStream(f), tOut);
and the code crashed with a "Too many open files" error at the OS level
The following minor change fix the problem:
FileInputStream in = new FileInputStream(f);
IOUtils.copy(in, tOut);
in.close();
I haven't figured out what exactly was going wrong but a scouring of google caches I found a working example. Sorry for the tumbleweed!
public void CreateTarGZ()
throws FileNotFoundException, IOException
{
try {
System.out.println(new File(".").getAbsolutePath());
dirPath = "parent/childDirToCompress/";
tarGzPath = "archive.tar.gz";
fOut = new FileOutputStream(new File(tarGzPath));
bOut = new BufferedOutputStream(fOut);
gzOut = new GzipCompressorOutputStream(bOut);
tOut = new TarArchiveOutputStream(gzOut);
addFileToTarGz(tOut, dirPath, "");
} finally {
tOut.finish();
tOut.close();
gzOut.close();
bOut.close();
fOut.close();
}
}
private void addFileToTarGz(TarArchiveOutputStream tOut, String path, String base)
throws IOException
{
File f = new File(path);
System.out.println(f.exists());
String entryName = base + f.getName();
TarArchiveEntry tarEntry = new TarArchiveEntry(f, entryName);
tOut.putArchiveEntry(tarEntry);
if (f.isFile()) {
IOUtils.copy(new FileInputStream(f), tOut);
tOut.closeArchiveEntry();
} else {
tOut.closeArchiveEntry();
File[] children = f.listFiles();
if (children != null) {
for (File child : children) {
System.out.println(child.getName());
addFileToTarGz(tOut, child.getAbsolutePath(), entryName + "/");
}
}
}
}
I ended up doing the following:
public URL createTarGzip() throws IOException {
Path inputDirectoryPath = ...
File outputFile = new File("/path/to/filename.tar.gz");
try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
GzipCompressorOutputStream gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream);
TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(gzipOutputStream)) {
tarArchiveOutputStream.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
tarArchiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
List<File> files = new ArrayList<>(FileUtils.listFiles(
inputDirectoryPath,
new RegexFileFilter("^(.*?)"),
DirectoryFileFilter.DIRECTORY
));
for (int i = 0; i < files.size(); i++) {
File currentFile = files.get(i);
String relativeFilePath = new File(inputDirectoryPath.toUri()).toURI().relativize(
new File(currentFile.getAbsolutePath()).toURI()).getPath();
TarArchiveEntry tarEntry = new TarArchiveEntry(currentFile, relativeFilePath);
tarEntry.setSize(currentFile.length());
tarArchiveOutputStream.putArchiveEntry(tarEntry);
tarArchiveOutputStream.write(IOUtils.toByteArray(new FileInputStream(currentFile)));
tarArchiveOutputStream.closeArchiveEntry();
}
tarArchiveOutputStream.close();
return outputFile.toURI().toURL();
}
}
This takes care of the some of the edge cases that come up in the other solutions.
I had to make some adjustments to #merrick solution to get it to work related to the path. Perhaps with the latest maven dependencies. The currently accepted solution didn't work for me.
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
import org.apache.commons.compress.compressors.gzip.GzipCompressorOutputStream;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.filefilter.DirectoryFileFilter;
import org.apache.commons.io.filefilter.RegexFileFilter;
public class TAR {
public static void CreateTarGZ(String inputDirectoryPath, String outputPath) throws IOException {
File inputFile = new File(inputDirectoryPath);
File outputFile = new File(outputPath);
try (FileOutputStream fileOutputStream = new FileOutputStream(outputFile);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
GzipCompressorOutputStream gzipOutputStream = new GzipCompressorOutputStream(bufferedOutputStream);
TarArchiveOutputStream tarArchiveOutputStream = new TarArchiveOutputStream(gzipOutputStream)) {
tarArchiveOutputStream.setBigNumberMode(TarArchiveOutputStream.BIGNUMBER_POSIX);
tarArchiveOutputStream.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
List<File> files = new ArrayList<>(FileUtils.listFiles(
inputFile,
new RegexFileFilter("^(.*?)"),
DirectoryFileFilter.DIRECTORY
));
for (int i = 0; i < files.size(); i++) {
File currentFile = files.get(i);
String relativeFilePath = inputFile.toURI().relativize(
new File(currentFile.getAbsolutePath()).toURI()).getPath();
TarArchiveEntry tarEntry = new TarArchiveEntry(currentFile, relativeFilePath);
tarEntry.setSize(currentFile.length());
tarArchiveOutputStream.putArchiveEntry(tarEntry);
tarArchiveOutputStream.write(IOUtils.toByteArray(new FileInputStream(currentFile)));
tarArchiveOutputStream.closeArchiveEntry();
}
tarArchiveOutputStream.close();
}
}
}
Maven
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.18</version>
</dependency>
Something I use (via Files.walk API), you can chain gzip(tar(youFile));
public static File gzip(File fileToCompress) throws IOException {
final File gzipFile = new File(fileToCompress.toPath().getParent().toFile(),
fileToCompress.getName() + ".gz");
final byte[] buffer = new byte[1024];
try (FileInputStream in = new FileInputStream(fileToCompress);
GZIPOutputStream out = new GZIPOutputStream(
new FileOutputStream(gzipFile))) {
int len;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
return gzipFile;
}
public static File tar(File folderToCompress) throws IOException, ArchiveException {
final File tarFile = Files.createTempFile(null, ".tar").toFile();
try (TarArchiveOutputStream out = (TarArchiveOutputStream) new ArchiveStreamFactory()
.createArchiveOutputStream(ArchiveStreamFactory.TAR,
new FileOutputStream(tarFile))) {
out.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
Files.walk(folderToCompress.toPath()) //
.forEach(source -> {
if (source.toFile().isFile()) {
final String relatifSourcePath = StringUtils.substringAfter(
source.toString(), folderToCompress.getPath());
final TarArchiveEntry entry = new TarArchiveEntry(
source.toFile(), relatifSourcePath);
try (InputStream in = new FileInputStream(source.toFile())){
out.putArchiveEntry(entry);
IOUtils.copy(in, out);
out.closeArchiveEntry();
}
catch (IOException e) {
// Handle this better than bellow...
throw new RuntimeException(e);
}
}
});
}
return tarFile;
}
Check below for Apache commons-compress and File walker examples.
This example tar.gz a directory.
public static void createTarGzipFolder(Path source) throws IOException {
if (!Files.isDirectory(source)) {
throw new IOException("Please provide a directory.");
}
// get folder name as zip file name
String tarFileName = source.getFileName().toString() + ".tar.gz";
try (OutputStream fOut = Files.newOutputStream(Paths.get(tarFileName));
BufferedOutputStream buffOut = new BufferedOutputStream(fOut);
GzipCompressorOutputStream gzOut = new GzipCompressorOutputStream(buffOut);
TarArchiveOutputStream tOut = new TarArchiveOutputStream(gzOut)) {
Files.walkFileTree(source, new SimpleFileVisitor<>() {
#Override
public FileVisitResult visitFile(Path file,
BasicFileAttributes attributes) {
// only copy files, no symbolic links
if (attributes.isSymbolicLink()) {
return FileVisitResult.CONTINUE;
}
// get filename
Path targetFile = source.relativize(file);
try {
TarArchiveEntry tarEntry = new TarArchiveEntry(
file.toFile(), targetFile.toString());
tOut.putArchiveEntry(tarEntry);
Files.copy(file, tOut);
tOut.closeArchiveEntry();
System.out.printf("file : %s%n", file);
} catch (IOException e) {
System.err.printf("Unable to tar.gz : %s%n%s%n", file, e);
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.printf("Unable to tar.gz : %s%n%s%n", file, exc);
return FileVisitResult.CONTINUE;
}
});
tOut.finish();
}
}
This example extracts a tar.gz, and checks zip slip attack.
public static void decompressTarGzipFile(Path source, Path target)
throws IOException {
if (Files.notExists(source)) {
throw new IOException("File doesn't exists!");
}
try (InputStream fi = Files.newInputStream(source);
BufferedInputStream bi = new BufferedInputStream(fi);
GzipCompressorInputStream gzi = new GzipCompressorInputStream(bi);
TarArchiveInputStream ti = new TarArchiveInputStream(gzi)) {
ArchiveEntry entry;
while ((entry = ti.getNextEntry()) != null) {
Path newPath = zipSlipProtect(entry, target);
if (entry.isDirectory()) {
Files.createDirectories(newPath);
} else {
// check parent folder again
Path parent = newPath.getParent();
if (parent != null) {
if (Files.notExists(parent)) {
Files.createDirectories(parent);
}
}
// copy TarArchiveInputStream to Path newPath
Files.copy(ti, newPath, StandardCopyOption.REPLACE_EXISTING);
}
}
}
}
private static Path zipSlipProtect(ArchiveEntry entry, Path targetDir)
throws IOException {
Path targetDirResolved = targetDir.resolve(entry.getName());
Path normalizePath = targetDirResolved.normalize();
if (!normalizePath.startsWith(targetDir)) {
throw new IOException("Bad entry: " + entry.getName());
}
return normalizePath;
}
References
https://mkyong.com/java/how-to-create-tar-gz-in-java/
https://commons.apache.org/proper/commons-compress/examples.html

Categories

Resources