/** * */ package ma.car.tishadow.bundle.update.util; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.LinkedList; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.appcelerator.kroll.KrollFunction; import org.appcelerator.kroll.KrollObject; import org.appcelerator.kroll.common.Log; import org.appcelerator.titanium.io.TiBaseFile; import org.appcelerator.titanium.io.TiFileFactory; /** * Represents an utility for basic compression/decompression in '.zip' extension. The implementation in this class is an extension of 'ti.compression' module, * and add extra ability to compress/de-compress asynchronized, will be modified later to expose proper behaviors. * @author wei.ding * @see {@link CompressionModule} */ public final class ZipUtil { private static final String SOURCE = "source"; private static final String SRC = "src"; private static final String DESTINATION = "destination"; private static final String DEST = "dest"; private static final String OVERWRITE = "overwrite"; private static final ExecutorService POOL = Executors.newCachedThreadPool(); /** * Compresses a list of files into a simple zip file. * @param dest the destination zip file. * @param src the files which will be compressed. * @return status message. */ public static String compress(String dest, Object[] src) { if (dest == null || dest.length() == 0) { return "archiveFile was not specified or was empty!"; } TiBaseFile zip = TiFileFactory.createTitaniumFile(dest, false); if (src == null || src.length == 0) { return "fileArray was not specified or was empty!"; } LinkedList<TiBaseFile> files = new LinkedList<TiBaseFile>(); for (Object rawFile : src) { files.add(TiFileFactory.createTitaniumFile(rawFile.toString(), false)); } // And then zip the files in to the archive. try { OutputStream fout = zip.getOutputStream(); ZipOutputStream zout = new ZipOutputStream(new BufferedOutputStream(fout)); while (!files.isEmpty()) { TiBaseFile file = files.remove(); if (!file.exists()) { Log.e("ZipUtil", "Skipping over file, because it does not exist: " + file.nativePath()); } else { ZipEntry ze = new ZipEntry(file.name()); zout.putNextEntry(ze); writeInFile(file, zout); zout.closeEntry(); } } zout.close(); return "success"; } catch (Exception e) { Log.e("ZipUtil", "Hit exception while unzipping the archive!", e); return e.toString(); } } /** * Compress a list of files into a simple zip file asynchronized. */ @SuppressWarnings("rawtypes") public static void compressAsyn(final HashMap arguments, final KrollFunction callback, final KrollObject context) { POOL.execute(new Runnable() { @SuppressWarnings({ "unchecked" }) @Override public void run() { String dest = (String) getValue(arguments, DEST, this.getValue(arguments, DESTINATION, null)); String[] src = (String[]) getValue(arguments, SRC, this.getValue(arguments, SOURCE, null)); String message = compress(dest, src); HashMap callbackArgs = new HashMap(); callbackArgs.put("message", message); if (callback != null) { callback.callAsync(context, callbackArgs); } } private Object getValue(HashMap map, Object key, Object defaultValue) { return map.containsKey(key) ? map.get(key) : defaultValue; } }); } /** * Decompresses a zip file into another directory. * @param dest the destination directory which will contanin all decompressed files. * @param src the source zip file. * @param overwrite, true if decompression can overwrite the same file into the destination directory, otherwise not. * @return */ public static String decompress(String dest, String src, Boolean overwrite) { if (dest == null || dest.length() == 0) { return "destinationFolder was not specified or was empty!"; } String destPath = TiFileFactory.createTitaniumFile(dest, false).getNativeFile().getAbsolutePath(); if (src == null || src.length() == 0) { return "archiveFile was not specified or was empty!"; } TiBaseFile zip = TiFileFactory.createTitaniumFile(src, false); if (!zip.exists()) { return "archiveFile was not found at " + src + "!"; } if (overwrite == null) { return "overwrite was not specified!"; } // And then unzip the archive. try { InputStream fin = zip.getInputStream(); ZipInputStream zin = new ZipInputStream(new BufferedInputStream(fin)); ZipEntry ze = null; while ((ze = zin.getNextEntry()) != null) { String target = destPath + "/" + ze.getName(); if (ze.isDirectory()) { ensureDirectoryExists(target); } else { File file = new File(target); if (overwrite || !file.exists()) { writeOutFile(file, target, zin); } } } zin.close(); fin.close(); return "success"; } catch (Exception e) { Log.e("ZipUtil", "Hit exception while unzipping the archive!", e); return e.toString(); } } @SuppressWarnings("rawtypes") public static void decompressAsyn(final HashMap arguments, final KrollFunction callback, final KrollObject context) { POOL.execute(new Runnable() { @SuppressWarnings({ "unchecked" }) @Override public void run() { String dest = (String) getValue(arguments, DEST, this.getValue(arguments, DESTINATION, null)); String src = (String) getValue(arguments, SRC, this.getValue(arguments, SOURCE, null)); Boolean overwrite = (Boolean) getValue(arguments, OVERWRITE, null); String message = decompress(dest, src, overwrite); HashMap callbackArgs = new HashMap(); callbackArgs.put("message", message); if (callback != null) { callback.callAsync(context, callbackArgs); } } private Object getValue(HashMap map, Object key, Object defaultValue) { return map.containsKey(key) ? map.get(key) : defaultValue; } }); } public static void ensureDirectoryExists(String target) { File f = new File(target); if (!f.isDirectory()) { f.mkdirs(); } } public static String unzip(Object[] args) { // Check that our arguments are valid. if (args.length != 3) { return "Invalid number of arguments provided!"; } String dest = (String) args[0]; String src = (String) args[1]; Boolean overwrite = (Boolean) args[2]; return decompress(dest, src, overwrite); } @SuppressWarnings("rawtypes") public static void unzipAsyn(HashMap arguments, KrollFunction callback, KrollObject context) { decompressAsyn(arguments, callback, context); } /** * Compress a list of files into a single zip file. * @param args * @return */ public static String zip(Object[] args) { // Check that our arguments are valid. if (args.length != 2) { return "Invalid number of arguments provided!"; } String dest = (String) args[0]; Object[] src = (Object[]) args[1]; return compress(dest, src); } @SuppressWarnings("rawtypes") public static void zipAsyn(HashMap arguments, KrollFunction callback, KrollObject context) { compressAsyn(arguments, callback, context); } private static void writeInFile(TiBaseFile tifile, ZipOutputStream zout) throws IOException { int size; byte[] buffer = new byte[2048]; InputStream fos = tifile.getInputStream(); BufferedInputStream bos = new BufferedInputStream(fos, buffer.length); while ((size = bos.read(buffer, 0, buffer.length)) != -1) { zout.write(buffer, 0, size); } } private static void writeOutFile(File file, String target, ZipInputStream zin) throws IOException { // Make sure the parent directory exists. file.getParentFile().mkdirs(); // Write out the file. int size; byte[] buffer = new byte[2048]; FileOutputStream fos = new FileOutputStream(target); BufferedOutputStream bos = new BufferedOutputStream(fos, buffer.length); while ((size = zin.read(buffer, 0, buffer.length)) != -1) { bos.write(buffer, 0, size); } bos.flush(); bos.close(); } }