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      */
createZip(File dir, File zipFile)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      */
addToZip(ZipOutputStream out, File file, List<String> relativePathSegs)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      */
buildPath(List<String> relativePathSegs)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      */
writeToStream(File file, OutputStream out)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