001/*
002 * (C) Copyright 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     Nuxeo - initial API and implementation
018 *
019 *
020 * $$Id$$
021 */
022
023package org.nuxeo.common.utils;
024
025import java.io.BufferedInputStream;
026import java.io.BufferedOutputStream;
027import java.io.File;
028import java.io.FileInputStream;
029import java.io.FileOutputStream;
030import java.io.IOException;
031import java.io.InputStream;
032import java.io.OutputStream;
033import java.net.URL;
034import java.util.ArrayList;
035import java.util.Enumeration;
036import java.util.List;
037import java.util.zip.ZipEntry;
038import java.util.zip.ZipFile;
039import java.util.zip.ZipInputStream;
040import java.util.zip.ZipOutputStream;
041
042/**
043 * @author bstefanescu
044 */
045public final class ZipUtils {
046
047    // This is an utility class
048    private ZipUtils() {
049    }
050
051    // _____________________________ ZIP ________________________________
052
053    public static void _putDirectoryEntry(String entryName, ZipOutputStream out) throws IOException {
054        ZipEntry zentry = new ZipEntry(entryName + '/');
055        out.putNextEntry(zentry);
056        out.closeEntry();
057    }
058
059    public static void _putFileEntry(File file, String entryName, ZipOutputStream out) throws IOException {
060        FileInputStream in = null;
061        try {
062            in = new FileInputStream(file);
063            _zip(entryName, in, out);
064        } finally {
065            if (in != null) {
066                in.close();
067            }
068        }
069    }
070
071    public static void _zip(String entryName, InputStream in, ZipOutputStream out) throws IOException {
072        ZipEntry zentry = new ZipEntry(entryName);
073        out.putNextEntry(zentry);
074        // Transfer bytes from the input stream to the ZIP file
075        FileUtils.copy(in, out);
076        out.closeEntry();
077    }
078
079    public static void _zip(String entryName, File file, ZipOutputStream out) throws IOException {
080        // System.out.println("Compressing "+entryName);
081        if (file.isDirectory()) {
082            entryName += '/';
083            ZipEntry zentry = new ZipEntry(entryName);
084            out.putNextEntry(zentry);
085            out.closeEntry();
086            File[] files = file.listFiles();
087            for (int i = 0, len = files.length; i < len; i++) {
088                _zip(entryName + files[i].getName(), files[i], out);
089            }
090        } else {
091            InputStream in = null;
092            try {
093                in = new BufferedInputStream(new FileInputStream(file));
094                _zip(entryName, in, out);
095            } finally {
096                if (in != null) {
097                    in.close();
098                }
099            }
100        }
101    }
102
103    public static void _zip(File[] files, ZipOutputStream out, String prefix) throws IOException {
104        if (prefix != null) {
105            int len = prefix.length();
106            if (len == 0) {
107                prefix = null;
108            } else if (prefix.charAt(len - 1) != '/') {
109                prefix += '/';
110            }
111        }
112        for (int i = 0, len = files.length; i < len; i++) {
113            String name = prefix != null ? prefix + files[i].getName() : files[i].getName();
114            _zip(name, files[i], out);
115        }
116    }
117
118    public static void zip(File file, OutputStream out, String prefix) throws IOException {
119        if (prefix != null) {
120            int len = prefix.length();
121            if (len == 0) {
122                prefix = null;
123            } else if (prefix.charAt(len - 1) != '/') {
124                prefix += '/';
125            }
126        }
127        String name = prefix != null ? prefix + file.getName() : file.getName();
128        ZipOutputStream zout = null;
129        try {
130            zout = new ZipOutputStream(out);
131            _zip(name, file, zout);
132        } finally {
133            if (zout != null) {
134                zout.finish();
135            }
136        }
137    }
138
139    public static void zip(File[] files, OutputStream out, String prefix) throws IOException {
140        ZipOutputStream zout = null;
141        try {
142            zout = new ZipOutputStream(out);
143            _zip(files, zout, prefix);
144        } finally {
145            if (zout != null) {
146                zout.finish();
147            }
148        }
149    }
150
151    public static void zip(File file, File zip) throws IOException {
152        OutputStream out = null;
153        try {
154            out = new BufferedOutputStream(new FileOutputStream(zip));
155            zip(file, out, null);
156        } finally {
157            if (out != null) {
158                out.close();
159            }
160        }
161    }
162
163    public static void zip(File[] files, File zip) throws IOException {
164        OutputStream out = null;
165        try {
166            out = new BufferedOutputStream(new FileOutputStream(zip));
167            zip(files, out, null);
168        } finally {
169            if (out != null) {
170                out.close();
171            }
172        }
173    }
174
175    public static void zip(File file, File zip, String prefix) throws IOException {
176        OutputStream out = null;
177        try {
178            out = new BufferedOutputStream(new FileOutputStream(zip));
179            zip(file, out, prefix);
180        } finally {
181            if (out != null) {
182                out.close();
183            }
184        }
185    }
186
187    public static void zip(File[] files, File zip, String prefix) throws IOException {
188        OutputStream out = null;
189        try {
190            out = new BufferedOutputStream(new FileOutputStream(zip));
191            zip(files, out, prefix);
192        } finally {
193            if (out != null) {
194                out.close();
195            }
196        }
197    }
198
199    public static void zipFilesUsingPrefix(String prefix, File[] files, OutputStream out) throws IOException {
200        ZipOutputStream zout = null;
201        try {
202            zout = new ZipOutputStream(out);
203            if (prefix != null && prefix.length() > 0) {
204                int p = prefix.indexOf('/');
205                while (p > -1) {
206                    _putDirectoryEntry(prefix.substring(0, p), zout);
207                    p = prefix.indexOf(p + 1, '/');
208                }
209                _putDirectoryEntry(prefix, zout);
210                prefix += '/';
211            } else {
212                prefix = "";
213            }
214            // prefix = prefix + '/';
215            for (File file : files) {
216                _putFileEntry(file, prefix + file.getName(), zout);
217            }
218        } finally {
219            if (zout != null) {
220                zout.finish();
221            }
222        }
223    }
224
225    // _____________________________ UNZIP ________________________________
226
227    public static void unzip(String prefix, InputStream zipStream, File dir) throws IOException {
228        ZipInputStream in = null;
229        try {
230            in = new ZipInputStream(new BufferedInputStream(zipStream));
231            unzip(prefix, in, dir);
232        } finally {
233            if (in != null) {
234                in.close();
235            }
236        }
237    }
238
239    public static void unzip(InputStream zipStream, File dir) throws IOException {
240        ZipInputStream in = null;
241        try {
242            in = new ZipInputStream(new BufferedInputStream(zipStream));
243            unzip(in, dir);
244        } finally {
245            if (in != null) {
246                in.close();
247            }
248        }
249    }
250
251    public static void unzip(String prefix, URL zip, File dir) throws IOException {
252        ZipInputStream in = null;
253        try {
254            in = new ZipInputStream(new BufferedInputStream(zip.openStream()));
255            unzip(prefix, in, dir);
256        } finally {
257            if (in != null) {
258                in.close();
259            }
260        }
261    }
262
263    public static void unzip(URL zip, File dir) throws IOException {
264        ZipInputStream in = null;
265        try {
266            in = new ZipInputStream(new BufferedInputStream(zip.openStream()));
267            unzip(in, dir);
268        } finally {
269            if (in != null) {
270                in.close();
271            }
272        }
273    }
274
275    public static void unzip(String prefix, File zip, File dir) throws IOException {
276        ZipInputStream in = null;
277        try {
278            in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)));
279            unzip(prefix, in, dir);
280        } finally {
281            if (in != null) {
282                in.close();
283            }
284        }
285    }
286
287    public static void unzip(File zip, File dir) throws IOException {
288        ZipInputStream in = null;
289        try {
290            in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)));
291            unzip(in, dir);
292        } finally {
293            if (in != null) {
294                in.close();
295            }
296        }
297    }
298
299    public static void unzip(String prefix, ZipInputStream in, File dir) throws IOException {
300        dir.mkdirs();
301        ZipEntry entry;
302        while ((entry  = in.getNextEntry()) != null) {
303            String entryName = entry.getName();
304            if (!entryName.startsWith(prefix) || entryName.contains("..")) {
305                continue;
306            }
307
308            File file = new File(dir, entryName.substring(prefix.length()));
309            if (entry.isDirectory()) {
310                file.mkdirs();
311            } else {
312                file.getParentFile().mkdirs();
313                FileUtils.copyToFile(in, file);
314            }
315        }
316    }
317
318    public static void unzip(ZipInputStream in, File dir) throws IOException {
319        dir.mkdirs();
320        ZipEntry entry;
321        while ((entry = in.getNextEntry()) != null) {
322            if (entry.getName().contains("..")) {
323                continue;
324            }
325
326            File file = new File(dir, entry.getName());
327            if (entry.isDirectory()) {
328                file.mkdirs();
329            } else {
330                file.getParentFile().mkdirs();
331                FileUtils.copyToFile(in, file);
332            }
333        }
334    }
335
336    public static void unzipIgnoreDirs(ZipInputStream in, File dir) throws IOException {
337        dir.mkdirs();
338        ZipEntry entry = in.getNextEntry();
339        while (entry != null) {
340            String entryName = entry.getName();
341            if (entry.isDirectory()) {
342            } else {
343                int p = entryName.lastIndexOf('/');
344                if (p > -1) {
345                    entryName = entryName.substring(p + 1);
346                }
347                File file = new File(dir, entryName);
348                FileUtils.copyToFile(in, file);
349            }
350            entry = in.getNextEntry();
351        }
352    }
353
354    public static void unzipIgnoreDirs(InputStream zipStream, File dir) throws IOException {
355        ZipInputStream in = null;
356        try {
357            in = new ZipInputStream(new BufferedInputStream(zipStream));
358            unzipIgnoreDirs(in, dir);
359        } finally {
360            if (in != null) {
361                in.close();
362            }
363        }
364    }
365
366    public static void unzip(File zip, File dir, PathFilter filter) throws IOException {
367        ZipInputStream in = null;
368        try {
369            in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)));
370            unzip(in, dir, filter);
371        } finally {
372            if (in != null) {
373                in.close();
374            }
375        }
376    }
377
378    public static void unzip(ZipInputStream in, File dir, PathFilter filter) throws IOException {
379        if (filter == null) {
380            unzip(in, dir);
381            return;
382        }
383        ZipEntry entry;
384        while ((entry = in.getNextEntry()) != null) {
385            String entryName = entry.getName();
386            if (entryName.contains("..")) {
387                continue;
388            }
389
390            if (filter.accept(new Path(entryName))) {
391                // System.out.println("Extracting "+entryName);
392                File file = new File(dir, entryName);
393                if (entry.isDirectory()) {
394                    file.mkdirs();
395                } else {
396                    file.getParentFile().mkdirs();
397                    FileUtils.copyToFile(in, file);
398                }
399            }
400        }
401    }
402
403    public static void unzip(String prefix, File zip, File dir, PathFilter filter) throws IOException {
404        ZipInputStream in = null;
405        try {
406            in = new ZipInputStream(new BufferedInputStream(new FileInputStream(zip)));
407            unzip(prefix, in, dir, filter);
408        } finally {
409            if (in != null) {
410                in.close();
411            }
412        }
413    }
414
415    public static void unzip(String prefix, ZipInputStream in, File dir, PathFilter filter) throws IOException {
416        if (filter == null) {
417            unzip(prefix, in, dir);
418            return;
419        }
420        dir.mkdirs();
421
422        ZipEntry entry;
423        while ((entry = in.getNextEntry()) != null) {
424            String entryName = entry.getName();
425            if (entryName.contains("..")) {
426                continue;
427            }
428
429            if (filter.accept(new Path(entryName))) {
430                if (!entry.getName().startsWith(prefix)) {
431                    continue;
432                }
433                File file = new File(dir, entry.getName().substring(prefix.length()));
434                if (entry.isDirectory()) {
435                    file.mkdirs();
436                } else {
437                    file.getParentFile().mkdirs();
438                    FileUtils.copyToFile(in, file);
439                }
440            }
441        }
442    }
443
444    // ________________ Entries ________________
445    /**
446     * Unzip directly the entry. The returned InputStream has to be closed.
447     *
448     * @return the input stream of the desired entry - has to be closed by the caller, or null if not found
449     * @param file the source file
450     * @param entryName the entry name that has to be extracted
451     */
452    public static InputStream getEntryContentAsStream(File file, String entryName) throws IOException {
453        InputStream result = null;
454        ZipFile zip = new ZipFile(file);
455        ZipEntry entry = zip.getEntry(entryName);
456        if (entry != null) {
457            result = zip.getInputStream(entry);
458        }
459        return result;
460    }
461
462    /**
463     * Unzip directly the entry.
464     *
465     * @return the String content of the entry with name entryName
466     * @param file the source file
467     * @param entryName the entry name that has to be extracted
468     */
469    public static String getEntryContentAsString(File file, String entryName) throws IOException {
470        InputStream resultStream = getEntryContentAsStream(file, entryName);
471        // resultStream has been closed by FileUtils call
472        return FileUtils.read(resultStream);
473    }
474
475    /**
476     * Unzips directly the entry.
477     *
478     * @return The byte array content of the entry with name entryName
479     * @param file the source file
480     * @param entryName the entry name that has to be extracted
481     */
482    public static byte[] getEntryContentAsBytes(File file, String entryName) throws IOException {
483        InputStream resultStream = getEntryContentAsStream(file, entryName);
484        // resultStream has been closed by FileUtils call
485        return FileUtils.readBytes(resultStream);
486    }
487
488    /**
489     * Lists the entries on the zip file.
490     *
491     * @param file The zip file
492     * @return The list of entries
493     */
494    public static List<String> getEntryNames(File file) throws IOException {
495        List<String> result = new ArrayList<String>();
496        ZipFile zip = new ZipFile(file);
497        try {
498            Enumeration<? extends ZipEntry> entries = zip.entries();
499            while (entries.hasMoreElements()) {
500                ZipEntry entry = entries.nextElement();
501                result.add(entry.getName());
502            }
503        } finally {
504            zip.close();
505        }
506        return result;
507    }
508
509    /**
510     * Checks if a zip file contains a specified entry name.
511     *
512     * @param file the zip file
513     * @param entryName The content to be checked
514     * @return True if the file contains entryName. False otherwise
515     */
516    public static boolean hasEntry(File file, String entryName) throws IOException {
517        List<String> elements = getEntryNames(file);
518        return elements.contains(entryName);
519    }
520
521    public static InputStream getEntryContentAsStream(InputStream stream, String entryName) throws IOException {
522        ZipInputStream zip = new ZipInputStream(stream);
523        ZipEntry entry = zip.getNextEntry();
524        while (entry != null) {
525            if (entry.getName().equals(entryName)) {
526                return zip;
527            }
528            entry = zip.getNextEntry();
529        }
530        return null;
531    }
532
533    public static String getEntryContentAsString(InputStream stream, String searchedEntryName) throws IOException {
534        InputStream resultStream = getEntryContentAsStream(stream, searchedEntryName);
535        // resultStream has been closed by FileUtils call
536        return FileUtils.read(resultStream);
537    }
538
539    public static byte[] getEntryContentAsBytes(InputStream stream, String searchedEntryName) throws IOException {
540        InputStream resultStream = getEntryContentAsStream(stream, searchedEntryName);
541        // resultStream has been closed by FileUtils call
542        return FileUtils.readBytes(resultStream);
543    }
544
545    public static List<String> getEntryNames(InputStream stream) throws IOException {
546
547        List<String> result = new ArrayList<String>();
548        ZipInputStream zip = new ZipInputStream(stream);
549        try {
550            while (zip.available() == 1) {
551                ZipEntry entry = zip.getNextEntry();
552                if (entry != null) {
553                    result.add(entry.getName());
554                }
555            }
556        } finally {
557            zip.close();
558        }
559        return result;
560    }
561
562    public static boolean hasEntry(InputStream stream, String entryName) throws IOException {
563        List<String> elements = getEntryNames(stream);
564        return elements.contains(entryName);
565    }
566
567    public static InputStream getEntryContentAsStream(URL url, String entryName) throws IOException {
568        return getEntryContentAsStream(url.openStream(), entryName);
569    }
570
571    public static String getEntryContentAsString(URL url, String entryName) throws IOException {
572        InputStream resultStream = getEntryContentAsStream(url, entryName);
573        // resultStream has been closed by FileUtils call
574        return FileUtils.read(resultStream);
575    }
576
577    public static byte[] getEntryContentAsBytes(URL url, String entryName) throws IOException {
578        InputStream resultStream = getEntryContentAsStream(url, entryName);
579        // resultStream has been closed by FileUtils call
580        return FileUtils.readBytes(resultStream);
581    }
582
583    public static List<String> getEntryNames(URL url) throws IOException {
584        return getEntryNames(url.openStream());
585    }
586
587    public static boolean hasEntry(URL url, String entryName) throws IOException {
588        return hasEntry(url.openStream(), entryName);
589    }
590
591}