Merge multiple Zips into one

@param string $output_zip_file
Destination zip
@param bool $separate (optional)
If true (default), files will be placed in different directories

@return bool
@throws ZipException
351362
public function merge(string $output_zip_file, bool $separate = true): bool {

        $pathinfo = pathinfo($output_zip_file);
        $temporary_folder = $pathinfo['dirname']."/".ManagerTools::getTemporaryFolder();

        try {

            $this->extract($temporary_folder, $separate);
            $zip = Zip::create($output_zip_file);
            $zip->add($temporary_folder, true)->close();
            ManagerTools::recursiveUnlink($temporary_folder);
            return true;

        } catch (ZipException $ze) {
            throw $ze;
        } catch (Exception $e) {
            throw $e;
        }

    }
1323828
public static function dir($sourcePath, $outZipPath)
    {
        $sourcePath = rtrim($sourcePath, "/");
        $zip = new ZipArchive();
        $zip->open($outZipPath, ZipArchive::CREATE);
        self::folderToZip($sourcePath, $zip, strlen($sourcePath . "/"));
        $zip->close();
    }
1003056
public static void unZipFiles(File zipFile, File outputFolder) throws IOException {
        InputStream is = new BufferedInputStream(new FileInputStream(zipFile));
        try {
            unZipFiles(is, outputFolder);
        } finally {
            IOUtils.closeQuietly(is);
        }
    }
650858
public void save(final OutputStream out) throws IOException {
        final ZipUTF8WriterBuilder builder = ZipUTF8WriterImpl.builder();
        final ZipUTF8Writer writer = builder.build(out);
        this.save(writer);
        writer.finish(); // ensures the zip file is well formed
        writer.flush();
    }
179727
public static void copyZipWithoutEmptyDirectories(final File inputFile, final File outputFile) throws IOException
    {
        final byte[] buf = new byte[0x2000];

        final ZipFile inputZip = new ZipFile(inputFile);
        final ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(outputFile));
        try
        {
            // read a the entries of the input zip file and sort them
            final Enumeration<? extends ZipEntry> e = inputZip.entries();
            final ArrayList<ZipEntry> sortedList = new ArrayList<ZipEntry>();
            while (e.hasMoreElements()) {
                final ZipEntry entry = e.nextElement();
                sortedList.add(entry);
            }

            Collections.sort(sortedList, new Comparator<ZipEntry>()
            {
                public int compare(ZipEntry o1, ZipEntry o2)
                {
                    String n1 = o1.getName(), n2 = o2.getName();
                    if (metaOverride(n1, n2)) {
                        return -1;
                    }
                    if (metaOverride(n2, n1)) {
                        return 1;
                    }
                    return n1.compareTo(n2);
                }

                // make sure that META-INF/MANIFEST.MF is always the first entry after META-INF/
                private boolean metaOverride(String n1, String n2) {
                    return (n1.startsWith("META-INF/") && !n2.startsWith("META-INF/"))
                        || (n1.equals("META-INF/MANIFEST.MF") && !n2.equals(n1) && !n2.equals("META-INF/"))
                        || (n1.equals("META-INF/") && !n2.equals(n1));
                }
            });

            // treat them again and write them in output, wenn they not are empty directories
            for (int i = sortedList.size()-1; i>=0; i--)
            {
                final ZipEntry inputEntry = sortedList.get(i);
                final String name = inputEntry.getName();
                final boolean isEmptyDirectory;
                if (inputEntry.isDirectory())
                {
                    if (i == sortedList.size()-1)
                    {
                        // no item afterwards; it was an empty directory
                        isEmptyDirectory = true;
                    }
                    else
                    {
                        final String nextName = sortedList.get(i+1).getName();
                        isEmptyDirectory  = !nextName.startsWith(name);
                    }
                }
                else
                {
                    isEmptyDirectory = false;
                }

                if (isEmptyDirectory)
                {
                    sortedList.remove(i);
                }
            }

            // finally write entries in normal order
            for (int i = 0; i < sortedList.size(); i++)
            {
                final ZipEntry inputEntry = sortedList.get(i);
                final ZipEntry outputEntry = new ZipEntry(inputEntry);
                outputStream.putNextEntry(outputEntry);
                ByteArrayOutputStream baos = new ByteArrayOutputStream();
                final InputStream is = inputZip.getInputStream(inputEntry);
                IoUtil.pipe(is, baos, buf);
                is.close();
                outputStream.write(baos.toByteArray());
            }
        } finally {
            outputStream.close();
            inputZip.close();
        }

    }
780814
private static void compress(File source, ZipArchiveOutputStream zipOut, String zipEntryName) throws IOException {

		String tempZipEntryName = $.notEmpty(zipEntryName) ? zipEntryName : source.getName();

		byte[] buff = new byte[512];
		int readLen = -1;
		if (source.isFile()) {
			// System.out.println("压缩文件：" + source.getAbsolutePath());
			ZipArchiveEntry entry = new ZipArchiveEntry(tempZipEntryName);
			zipOut.putArchiveEntry(entry);
			BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
			while ((readLen = bis.read(buff)) != -1) {
				zipOut.write(buff, 0, readLen);
			}
			zipOut.closeArchiveEntry();
			IOUtils.closeQuietly(bis);
		} else if (source.isDirectory()) {
			// System.out.println("**** 压缩目录：" + source.getAbsolutePath() +
			ZipArchiveEntry entry = new ZipArchiveEntry(tempZipEntryName.concat(File.separator));
			zipOut.putArchiveEntry(entry);
			zipOut.closeArchiveEntry();
			File[] files = source.listFiles();
			if ($.notEmpty(files)) {
				for (File file : files) {
					compress(file, zipOut, tempZipEntryName + File.separator + file.getName());
				}
			}
		}
	}
187155
public static List<String> unzip(final File zipFile, final String destination, String encoding) {
    List<String> fileNames = CollectUtils.newArrayList();
    String dest = destination;
    if (!destination.endsWith(File.separator)) {
      dest = destination + File.separator;
    }
    ZipFile file;
    try {
      file = null;
      if (null == encoding) file = new ZipFile(zipFile);
      else file = new ZipFile(zipFile, encoding);
      Enumeration<ZipArchiveEntry> en = file.getEntries();
      ZipArchiveEntry ze = null;
      while (en.hasMoreElements()) {
        ze = en.nextElement();
        File f = new File(dest, ze.getName());
        if (ze.isDirectory()) {
          f.mkdirs();
          continue;
        } else {
          f.getParentFile().mkdirs();
          InputStream is = file.getInputStream(ze);
          OutputStream os = new FileOutputStream(f);
          IOs.copy(is, os);
          is.close();
          os.close();
          fileNames.add(f.getAbsolutePath());
        }
      }
      file.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return fileNames;
  }
222236
protected function zipNative($sourceDirectory, $zipFile, array $options)
    {
        if (!$options['includeDotFiles'] && stripos(PHP_OS, 'WIN') === 0) {
            $this->zipPHP($sourceDirectory, $zipFile, $options);

            return;
        }
        $fs = $this->getFilesystem();
        $originalZipFile = $zipFile;
        $revertName = false;
        if (!strpos(basename($zipFile), '.')) {
            for ($i = 0; ; ++$i) {
                $tmp = "$zipFile-$i.zip";
                if (!$fs->exists($tmp)) {
                    $zipFile = $tmp;
                    break;
                }
            }
            if ($options['append']) {
                if (@$fs->move($originalZipFile, $zipFile) === false) {
                    throw new Exception(t('Failed to move a temporary file.'));
                }
                $revertName = true;
            }
        }
        try {
            $cmd = 'zip';
            $level = (isset($options['level']) && is_numeric($options['level'])) ? @intval($options['level']) : null;
            if ($level !== null && $level >= 0 && $level <= 9) {
                $cmd .= ' -' . $level;
            }
            $cmd .= ' -q'; // quiet mode, to avoid overflow of stdout
            $cmd .= ' -r'; // recurse into directories
            $cmd .= ' ' . escapeshellarg($zipFile); // destination ZIP archive
            if ($options['includeDotFiles']) {
                $cmd .= ' .* *';
            } else {
                $cmd .= ' * -x \*/.\*';
            }
            $rc = 1;
            $output = [];
            $prevDir = @getcwd();
            if ($prevDir === false) {
                throw new Exception(t('Failed to determine current directory'));
            }
            if (@chdir($sourceDirectory) === false) {
                throw new Exception(t('Failed to enter directory ' . $sourceDirectory));
            }
            @exec($cmd . ' 2>&1', $output, $rc);
            @chdir($prevDir);
            if ($rc !== 0) {
                $error = trim(implode("\n", $output)) ?: t('Unknown error compressing a directory');
                throw new Exception($error);
            }
        } catch (Exception $x) {
            if ($fs->exists($zipFile)) {
                if ($revertName) {
                    @$fs->move($zipFile, $originalZipFile);
                } elseif (!$options['append']) {
                    @$fs->delete([$zipFile]);
                }
            }
            throw $x;
        }
        if ($originalZipFile !== $zipFile) {
            if (@$fs->move($zipFile, $originalZipFile) === false) {
                @$fs->delete([$zipFile]);
                throw new Exception(t('Failed to move a temporary file.'));
            }
        }
    }
565412
public static void compress(File source, File target) throws IOException {

        ZipOutputStream zipOut = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(target)));
        ZipEntry zipEntry = new ZipEntry(source.getName());
        zipOut.putNextEntry(zipEntry);
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source), BUFFER);
        byte data[] = new byte[BUFFER];

        int count = 0;
        while ((count = bis.read(data, 0, BUFFER)) != -1) {
            zipOut.write(data, 0, count);
        }
        bis.close();
        zipOut.close();

    }
364790
protected function unzipPHP($zipFile, $destinationDirectory, array $options)
    {
        if (!class_exists('ZipArchive')) {
            throw new Exception('Unable to unzip files using ZipArchive. Please ensure the Zip extension is installed.');
        }
        $zip = new ZipArchive();
        try {
            $flags = 0;
            if (!$options['skipCheck']) {
                $flags |= ZipArchive::CHECKCONS;
            }
            $zipErr = @$zip->open($zipFile, $flags);
            if ($zipErr !== true) {
                throw new Exception($this->describeZipArchiveError($zip, $zipErr));
            }
            if (@$zip->extractTo($destinationDirectory) !== true) {
                throw new Exception($this->describeZipArchiveError($zip, ZipArchive::ER_OK));
            }
            @$zip->close();
            $zip = null;
        } catch (Exception $x) {
            if ($zip !== null) {
                try {
                    @$zip->close();
                } catch (\Exception $foo) {
                }
                $zip = null;
            }
            throw $x;
        }
    }
90643
public static function folderToZip($folder, &$zipFile, $exclusiveLength, $exclude = array('.git'), $prefix = '') {
	if (!file_exists($folder)) return;
    $handle = opendir($folder);
    while (false !== $f = readdir($handle)) {
      if ($f != '.' && $f != '..') {
		  
		if (in_array($f, $exclude)) continue;
		  
        $filePath = "$folder/$f";
        // Remove prefix from file path before add to zip.
        $localPath = $prefix.substr($filePath, $exclusiveLength);
        if (is_file($filePath)) {
          $zipFile->addFile($filePath, $localPath);
        } elseif (is_dir($filePath)) {
          // Add sub-directory.
          $zipFile->addEmptyDir($localPath);
          self::folderToZip($filePath, $zipFile, $exclusiveLength, $exclude, $prefix);
        }
      }
    }
    closedir($handle);
  }
423905
protected function fileExistsWithinZip($zipStreamURI)
    {
        $doesFileExists = false;

        $pattern = '/zip:\/\/([^#]+)#(.*)/';
        if (preg_match($pattern, $zipStreamURI, $matches)) {
            $zipFilePath = $matches[1];
            $innerFilePath = $matches[2];

            $zip = new \ZipArchive();
            if ($zip->open($zipFilePath) === true) {
                $doesFileExists = ($zip->locateName($innerFilePath) !== false);
                $zip->close();
            }
        }

        return $doesFileExists;
    }
833405
private function zip($source, $destination)
    {
        if (!extension_loaded('zip')) {
            throw new Exception("ZIP extension not available");
        }

        $source = realpath($source);
        if (!is_dir($source)) {
            throw new FileException("Source must be a directory.");
        }

        $zip = new ZipArchive();
        $shouldOverwrite = $this->isOverwrite() ? ZipArchive::OVERWRITE : 0;
        if (!$zip->open($destination, ZipArchive::CREATE | $shouldOverwrite)) {
            throw new FileException("Couldn't open zip file.");
        }

        /* @var $iterator RecursiveIteratorIterator|RecursiveDirectoryIterator */
        $dirIterator = new RecursiveDirectoryIterator($source, FilesystemIterator::SKIP_DOTS);
        $iterator = new RecursiveIteratorIterator($dirIterator, RecursiveIteratorIterator::SELF_FIRST);
        while ($iterator->valid()) {
            if ($iterator->isDir()) {
                $zip->addEmptyDir($iterator->getSubPathName());
            } else if ($iterator->isFile()) {
                $zip->addFromString($iterator->getSubPathName(), file_get_contents($iterator->key()));
            }
            $iterator->next();
        }

        return $zip->close();
    }
1106724
public static function zipDir($sourcePath, $outZipPath, $del = false, $delDir = false)
  {
    $pathInfo = pathInfo($sourcePath);
    $parentPath = $pathInfo['dirname'];
    $dirName = $pathInfo['basename'];

    $z = new \ZipArchive();
    $z->open($outZipPath, \ZipArchive::CREATE);
    $z->addEmptyDir($dirName);
    self::folderToZip($sourcePath, $z, strlen("$parentPath/"), $del);
    if(true===$delDir)rmdir($sourcePath);
    $z->close();
  }
761275
public static List<File> extract(File zipFile, File outputFolder) throws IOException {
    List<File> extracted = new ArrayList<File>();

    byte[] buffer = new byte[2048];

    if (!outputFolder.exists()) {
      outputFolder.mkdir();
    }

    ZipInputStream zipInput = new ZipInputStream(new FileInputStream(zipFile));

    ZipEntry zipEntry = zipInput.getNextEntry();

    while (zipEntry != null) {

      String neFileNameName = zipEntry.getName();
      File newFile = new File(outputFolder + File.separator + neFileNameName);

      newFile.getParentFile().mkdirs();

      if (!zipEntry.isDirectory()) {
        FileOutputStream fos = new FileOutputStream(newFile);

        int size;
        while ((size = zipInput.read(buffer)) > 0) {
          fos.write(buffer, 0, size);
        }

        fos.close();
        extracted.add(newFile);
      }

      zipEntry = zipInput.getNextEntry();
    }

    zipInput.closeEntry();
    zipInput.close();

    return extracted;

  }
961840
public static void mapZipFile(final byte[] zipFile, final Map<String, byte[]> output, final String keyPrefix,
            final String keyUnPrefix) {
        try {
            final String fixedKeyPrefix = keyPrefix == null ? "" : keyPrefix;
            final String fixedKeyUnPrefix = keyUnPrefix == null ? "" : keyUnPrefix;
            final ZipInputStream zf = new ZipInputStream(new ByteArrayInputStream(zipFile));

            ZipEntry ze = null;
            while ((ze = zf.getNextEntry()) != null) {
                final String name = ze.getName();
                final long size = ze.getSize();

                // see if we are working with a file or a directory
                if (size != 0) {
                    byte[] fileContents = new byte[0];
                    final byte[] fileBuffer = new byte[BUFFER_SIZE];

                    int bytesRead = 0;
                    while ((bytesRead = zf.read(fileBuffer, 0, BUFFER_SIZE)) != -1) fileContents = ArrayUtils.addAll(fileContents,
                            bytesRead == BUFFER_SIZE ? fileBuffer : ArrayUtils.subarray(fileBuffer, 0, bytesRead));

                    output.put(fixedKeyPrefix + name.replace(fixedKeyUnPrefix, ""), fileContents);
                }
            }

        } catch (final IOException ex) {
           LOG.error("Unable to read file contents", ex);
        }
    }
1267135
public function unzip($zipFile, $destinationDirectory, array $options = [])
    {
        $fs = $this->getFilesystem();
        $normalized = @realpath($zipFile);
        if ($normalized === false || !$fs->isFile($normalized)) {
            throw new Exception(t('Unable to find the ZIP file %s', $zipFile));
        }
        $zipFile = str_replace(DIRECTORY_SEPARATOR, '/', $normalized);
        $normalized = @realpath($destinationDirectory);
        if ($normalized === false || !$fs->isDirectory($normalized)) {
            throw new Exception(t('Unable to find the directory %s', $destinationDirectory));
        }
        if (!$fs->isWritable($normalized)) {
            throw new Exception(t('The directory "%s" is not writable', $destinationDirectory));
        }
        $destinationDirectory = str_replace(DIRECTORY_SEPARATOR, '/', $normalized);
        $options += [
            'skipCheck' => false,
        ];
        if ($this->mayUseNativeCommand('unzip')) {
            $this->unzipNative($zipFile, $destinationDirectory, $options);
        } else {
            $this->unzipPHP($zipFile, $destinationDirectory, $options);
        }
    }