3434import java .util .zip .ZipFile ;
3535import java .util .zip .ZipInputStream ;
3636import java .util .zip .ZipOutputStream ;
37+ import javax .annotation .Nullable ;
3738import org .apache .commons .io .FileUtils ;
3839import org .apache .commons .io .IOUtils ;
3940
41+ import static org .apache .commons .io .IOUtils .EOF ;
42+
4043/**
4144 * Utility to zip directories and unzip files.
4245 *
@@ -59,10 +62,31 @@ public static File unzip(File zip, File toDir) throws IOException {
5962 return unzip (zip , toDir , ze -> true );
6063 }
6164
65+ /**
66+ * Unzip a file input stream to a directory.
67+ *
68+ * @param zip the zip file input stream
69+ * @param toDir the target directory. It is created if needed.
70+ * @return the parameter {@code toDir}
71+ * @since 6.2
72+ */
6273 public static File unzip (InputStream zip , File toDir ) throws IOException {
6374 return unzip (zip , toDir , ze -> true );
6475 }
6576
77+ /**
78+ * Unzip a file input stream to a directory.
79+ *
80+ * @param zip the zip file input stream
81+ * @param toDir the target directory. It is created if needed.
82+ * @param unzipSizeThreshold The parameter to prevent unzip size to exceed threshold(in Bytes)
83+ * @return the parameter {@code toDir}
84+ * @since 9.10
85+ */
86+ public static File unzip (InputStream zip , File toDir , long unzipSizeThreshold ) throws IOException {
87+ return unzip (zip , toDir , unzipSizeThreshold , ze -> true );
88+ }
89+
6690 /**
6791 * Unzip a file to a directory.
6892 *
@@ -74,23 +98,63 @@ public static File unzip(InputStream zip, File toDir) throws IOException {
7498 * @since 6.2
7599 */
76100 public static File unzip (InputStream stream , File toDir , Predicate <ZipEntry > filter ) throws IOException {
101+ return unzip (stream , toDir , null , filter );
102+ }
103+
104+ /**
105+ * Unzip a file to a directory.
106+ *
107+ * @param stream the zip input file
108+ * @param toDir the target directory. It is created if needed.
109+ * @param unzipSizeThreshold optional parameter to prevent unzip size to exceed threshold(in Bytes)
110+ * @param filter filter zip entries so that only a subset of directories/files can be
111+ * extracted to target directory.
112+ * @return the parameter {@code toDir}
113+ * @since 9.10
114+ */
115+ public static File unzip (InputStream stream , File toDir , @ Nullable Long unzipSizeThreshold , Predicate <ZipEntry > filter ) throws IOException {
77116 if (!toDir .exists ()) {
78117 FileUtils .forceMkdir (toDir );
79118 }
80119
120+ long totalSizeArchive = 0 ;
121+
81122 Path targetDirNormalizedPath = toDir .toPath ().normalize ();
82123 try (ZipInputStream zipStream = new ZipInputStream (stream )) {
83124 ZipEntry entry ;
84125 while ((entry = zipStream .getNextEntry ()) != null ) {
85126 if (filter .test (entry )) {
86- unzipEntry (entry , zipStream , targetDirNormalizedPath );
127+ File target = getTargetFile (entry , targetDirNormalizedPath );
128+ if (target .isDirectory ()) {
129+ continue ;
130+ }
131+ if (unzipSizeThreshold != null ) {
132+ totalSizeArchive = extractZipEntry (zipStream , target , totalSizeArchive , unzipSizeThreshold );
133+ } else {
134+ copy (zipStream , target );
135+ }
87136 }
88137 }
89138 return toDir ;
90139 }
91140 }
92141
93- private static void unzipEntry (ZipEntry entry , ZipInputStream zipStream , Path targetDirNormalized ) throws IOException {
142+ private static long extractZipEntry (ZipInputStream zipStream , File target , long totalSizeArchive , long threshold ) throws IOException {
143+ int nBytes = -1 ;
144+ byte [] buffer = new byte [8192 ];
145+ try (OutputStream outputStream = new FileOutputStream (target )) {
146+ while (EOF != (nBytes = zipStream .read (buffer ))) {
147+ outputStream .write (buffer , 0 , nBytes );
148+ totalSizeArchive += nBytes ;
149+ if (totalSizeArchive > threshold ) {
150+ throw new IllegalStateException (String .format ("Decompression failed because unzipped size reached threshold: %s bytes" , threshold ));
151+ }
152+ }
153+ }
154+ return totalSizeArchive ;
155+ }
156+
157+ private static File getTargetFile (ZipEntry entry , Path targetDirNormalized ) throws IOException {
94158 File to = targetDirNormalized .resolve (entry .getName ()).toFile ();
95159 verifyInsideTargetDirectory (entry , to .toPath (), targetDirNormalized );
96160
@@ -99,8 +163,8 @@ private static void unzipEntry(ZipEntry entry, ZipInputStream zipStream, Path ta
99163 } else {
100164 File parent = to .getParentFile ();
101165 throwExceptionIfDirectoryIsNotCreatable (parent );
102- copy (zipStream , to );
103166 }
167+ return to ;
104168 }
105169
106170 private static void throwExceptionIfDirectoryIsNotCreatable (File to ) throws IOException {
@@ -161,7 +225,7 @@ private static void copy(ZipFile zipFile, ZipEntry entry, File to) throws IOExce
161225
162226 public static void zipDir (File dir , File zip ) throws IOException {
163227 try (OutputStream out = Files .newOutputStream (zip .toPath ());
164- ZipOutputStream zout = new ZipOutputStream (out )) {
228+ ZipOutputStream zout = new ZipOutputStream (out )) {
165229 doZipDir (dir , zout );
166230 }
167231 }
0 commit comments