Android - Unzip a folder?

I am using a modified version of Beginner's method that extends AsyncTask and can update Observers on the main thread. Byte by byte compression is extremely slow and should be avoided. Instead a more efficient approach is to copy large chunks of data to the output stream.

package com.blarg.webviewscroller;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Observable;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import org.apache.commons.io.IOUtils;

import android.os.AsyncTask;
import android.util.Log;

public class UnZipper extends Observable {

    private static final String TAG = "UnZip";
    private String mFileName, mFilePath, mDestinationPath;

    public UnZipper (String fileName, String filePath, String destinationPath) {
        mFileName = fileName;
        mFilePath = filePath;
        mDestinationPath = destinationPath;
    }

    public String getFileName () {
        return mFileName;
    }

    public String getFilePath() {
        return mFilePath;
    }

    public String getDestinationPath () {
        return mDestinationPath;
    }

    public void unzip () {
        String fullPath = mFilePath + "/" + mFileName + ".zip";
        Log.d(TAG, "unzipping " + mFileName + " to " + mDestinationPath);
        new UnZipTask().execute(fullPath, mDestinationPath);
    }

    private class UnZipTask extends AsyncTask<String, Void, Boolean> {

        @SuppressWarnings("rawtypes")
        @Override
        protected Boolean doInBackground(String... params) {
            String filePath = params[0];
            String destinationPath = params[1];

            File archive = new File(filePath);
            try {
                ZipFile zipfile = new ZipFile(archive);
                for (Enumeration e = zipfile.entries(); e.hasMoreElements();) {
                    ZipEntry entry = (ZipEntry) e.nextElement();
                    unzipEntry(zipfile, entry, destinationPath);
                }
            } catch (Exception e) {
                Log.e(TAG, "Error while extracting file " + archive, e);
                return false;
            }

            return true;
        }

        @Override
        protected void onPostExecute(Boolean result) {
            setChanged();
            notifyObservers();
        }

        private void unzipEntry(ZipFile zipfile, ZipEntry entry,
                String outputDir) throws IOException {

            if (entry.isDirectory()) {
                createDir(new File(outputDir, entry.getName()));
                return;
            }

            File outputFile = new File(outputDir, entry.getName());
            if (!outputFile.getParentFile().exists()) {
                createDir(outputFile.getParentFile());
            }

            Log.v(TAG, "Extracting: " + entry);
            BufferedInputStream inputStream = new BufferedInputStream(zipfile.getInputStream(entry));
            BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(outputFile));

            try {
                IOUtils.copy(inputStream, outputStream);
            } finally {
                outputStream.close();
                inputStream.close();
            }
        }

        private void createDir(File dir) {
            if (dir.exists()) {
                return;
            }
            Log.v(TAG, "Creating dir " + dir.getName());
            if (!dir.mkdirs()) {
                throw new RuntimeException("Can not create dir " + dir);
            }
        }
    }
} 

It is used by a class that implements Observer, such as:

private void unzipWebFile(String filename) {
    String unzipLocation = getExternalFilesDir(null) + "/unzipped";
    String filePath = Environment.getExternalStorageDirectory().toString();

    UnZipper unzipper = new UnZipper(filename, filePath, unzipLocation);
    unzipper.addObserver(this);
    unzipper.unzip();
}

Your observer will get an update(Observable observable, Object data) callback when the unzip finishes.


static Handler myHandler;
ProgressDialog myProgress;

public void unzipFile(File zipfile) {
        myProgress = ProgressDialog.show(getContext(), "Extract Zip",
                        "Extracting Files...", true, false);
        File zipFile = zipfile;
        String directory = null;
        directory = zipFile.getParent();
        directory = directory + "/";
        myHandler = new Handler() {

                @Override
                public void handleMessage(Message msg) {
                        // process incoming messages here
                        switch (msg.what) {
                        case 0:
                                // update progress bar
                                myProgress.setMessage("" + (String) msg.obj);
                                break;
                        case 1:
                                myProgress.cancel();
                                Toast toast = Toast.makeText(getContext(),
                                                "Zip extracted successfully", 
Toast.LENGTH_SHORT);
                                toast.show();
                                provider.refresh();
                                break;
                        case 2:
                                myProgress.cancel();
                                break;
                        }
                        super.handleMessage(msg);
                }

        };
        Thread workthread = new Thread(new UnZip(zipFile, directory));
        workthread.start();
}

public class UnZip implements Runnable {

        File archive;
        String outputDir;

        public UnZip(File ziparchive, String directory) {
                archive = ziparchive;
                outputDir = directory;
        }

        public void log(String log) {
                Log.v("unzip", log);
        }

        @SuppressWarnings("unchecked")
        public void run() {
                Message msg;
                try {
                        ZipFile zipfile = new ZipFile(archive);
                        for (Enumeration e = zipfile.entries(); 
e.hasMoreElements();) {
                                ZipEntry entry = (ZipEntry) e.nextElement();
                                msg = new Message();
                                msg.what = 0;
                                msg.obj = "Extracting " + entry.getName();
                                myHandler.sendMessage(msg);
                                unzipEntry(zipfile, entry, outputDir);
                        }
                } catch (Exception e) {
                        log("Error while extracting file " + archive);
                }
                msg = new Message();
                msg.what = 1;
                myHandler.sendMessage(msg);
        }

        @SuppressWarnings("unchecked")
        public void unzipArchive(File archive, String outputDir) {
                try {
                        ZipFile zipfile = new ZipFile(archive);
                        for (Enumeration e = zipfile.entries(); 
e.hasMoreElements();) {
                                ZipEntry entry = (ZipEntry) e.nextElement();
                                unzipEntry(zipfile, entry, outputDir);
                        }
                } catch (Exception e) {
                        log("Error while extracting file " + archive);
                }
        }

        private void unzipEntry(ZipFile zipfile, ZipEntry entry,
                        String outputDir) throws IOException {

                if (entry.isDirectory()) {
                        createDir(new File(outputDir, entry.getName()));
                        return;
                }

                File outputFile = new File(outputDir, entry.getName());
                if (!outputFile.getParentFile().exists()) {
                        createDir(outputFile.getParentFile());
                }

                log("Extracting: " + entry);
                BufferedInputStream inputStream = new 
BufferedInputStream(zipfile
                                .getInputStream(entry));
                BufferedOutputStream outputStream = new BufferedOutputStream(
                                new FileOutputStream(outputFile));

                try {
                        IOUtils.copy(inputStream, outputStream);
                } finally {
                        outputStream.close();
                        inputStream.close();
                }
        }

        private void createDir(File dir) {
                log("Creating dir " + dir.getName());
                if (!dir.mkdirs())
                        throw new RuntimeException("Can not create dir " + dir);
        }
}

This is what worked for me thanks people


just "addon" for @rich.e answer:

in doInBackground() after iterating through ZipEtries you should close the file, because sometimes you want do delete the file after unzipping it and it throws an exception if file was not closed:

try {
        ZipFile zipfile = new ZipFile(archive);
        int entries = zipfile.size();
        int total = 0;
        if(onZipListener != null)
            onZipListener.onUncompressStart(archive);

        for (Enumeration<?> e = zipfile.entries(); e.hasMoreElements();) {
            ZipEntry entry = (ZipEntry) e.nextElement();
            if(onZipListener != null)
                onZipListener.onUncompressProgress(archive, (int) (total++ * 100 / entries));
            unzipEntry(zipfile, entry, path);
        }
        zipfile.close();
    } catch (Exception e) {
        e.printStackTrace();
    }