Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.compatibility.common.util;
     18 
     19 import java.io.BufferedInputStream;
     20 import java.io.BufferedOutputStream;
     21 import java.io.File;
     22 import java.io.FileInputStream;
     23 import java.io.FileOutputStream;
     24 import java.io.IOException;
     25 import java.io.InputStream;
     26 import java.io.OutputStream;
     27 import java.util.LinkedList;
     28 import java.util.List;
     29 import java.util.zip.ZipEntry;
     30 import java.util.zip.ZipOutputStream;
     31 
     32 public class ZipUtil {
     33 
     34     /**
     35      * Utility method to create a zip file containing the given directory and
     36      * all its contents.
     37      *
     38      * @param dir the directory to zip
     39      * @param zipFile the zip file to create - it should not already exist
     40      * @throws IOException if failed to create zip file
     41      */
     42     public static void createZip(File dir, File zipFile) throws IOException {
     43         ZipOutputStream out = null;
     44         try {
     45             FileOutputStream fileStream = new FileOutputStream(zipFile);
     46             out = new ZipOutputStream(new BufferedOutputStream(fileStream));
     47             addToZip(out, dir, new LinkedList<String>());
     48         } catch (IOException e) {
     49             zipFile.delete();
     50             throw e;
     51         } catch (RuntimeException e) {
     52             zipFile.delete();
     53             throw e;
     54         } finally {
     55             out.close();
     56         }
     57     }
     58 
     59     /**
     60      * Recursively adds given file and its contents to ZipOutputStream
     61      *
     62      * @param out the {@link ZipOutputStream}
     63      * @param file the {@link File} to add to the stream
     64      * @param relativePathSegs the relative path of file, including separators
     65      * @throws IOException if failed to add file to zip
     66      */
     67     public static void addToZip(ZipOutputStream out, File file, List<String> relativePathSegs)
     68             throws IOException {
     69         relativePathSegs.add(file.getName());
     70         if (file.isDirectory()) {
     71             // note: it appears even on windows, ZipEntry expects '/' as a path separator
     72             relativePathSegs.add("/");
     73         }
     74         ZipEntry zipEntry = new ZipEntry(buildPath(relativePathSegs));
     75         out.putNextEntry(zipEntry);
     76         if (file.isFile()) {
     77             writeToStream(file, out);
     78         }
     79         out.closeEntry();
     80         if (file.isDirectory()) {
     81             // recursively add contents
     82             File[] subFiles = file.listFiles();
     83             if (subFiles == null) {
     84                 throw new IOException(String.format("Could not read directory %s",
     85                         file.getAbsolutePath()));
     86             }
     87             for (File subFile : subFiles) {
     88                 addToZip(out, subFile, relativePathSegs);
     89             }
     90             // remove the path separator
     91             relativePathSegs.remove(relativePathSegs.size()-1);
     92         }
     93         // remove the last segment, added at beginning of method
     94         relativePathSegs.remove(relativePathSegs.size()-1);
     95     }
     96 
     97     /**
     98      * Builds a file system path from a stack of relative path segments
     99      *
    100      * @param relativePathSegs the list of relative paths
    101      * @return a {@link String} containing all relativePathSegs
    102      */
    103     private static String buildPath(List<String> relativePathSegs) {
    104         StringBuilder pathBuilder = new StringBuilder();
    105         for (String segment : relativePathSegs) {
    106             pathBuilder.append(segment);
    107         }
    108         return pathBuilder.toString();
    109     }
    110 
    111     /**
    112      * Helper method to write input file contents to output stream.
    113      *
    114      * @param file the input {@link File}
    115      * @param out the {@link OutputStream}
    116      *
    117      * @throws IOException
    118      */
    119     private static void writeToStream(File file, OutputStream out) throws IOException {
    120         InputStream inputStream = null;
    121         try {
    122             inputStream = new BufferedInputStream(new FileInputStream(file));
    123             StreamUtil.copyStreams(inputStream, out);
    124         } finally {
    125             inputStream.close();
    126         }
    127     }
    128 
    129 }
    130