content: package com.liyi.sutils.utils;
  
  import android.text.TextUtils;
  
  import java.io.BufferedInputStream;
  import java.io.BufferedOutputStream;
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.util.ArrayList;
  import java.util.Enumeration;
  import java.util.List;
  import java.util.zip.ZipEntry;
  import java.util.zip.ZipFile;
  import java.util.zip.ZipOutputStream;
  
  /**
   * 压缩相关工具类
   */
  public final class ZipUtil {
      private static final int BUFFER_LEN = 8192;
  
      private ZipUtil() {
          throw new UnsupportedOperationException("cannot be instantiated");
      }
  
      /**
       * 压缩文件
       *
       * @param resFilePath 待压缩文件路径
       * @param zipFilePath 压缩文件路径
       * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
       * @throws IOException IO 错误时抛出
       */
      public static boolean zipFile(String resFilePath, String zipFilePath) throws IOException {
          return zipFile(resFilePath, zipFilePath, null);
      }
  
      /**
       * 压缩文件
       *
       * @param resFilePath 待压缩文件路径
       * @param zipFilePath 压缩文件路径
       * @param comment     压缩文件的注释
       * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
       * @throws IOException IO 错误时抛出
       */
      public static boolean zipFile(String resFilePath, String zipFilePath, String comment) throws IOException {
          return zipFile(getFileByPath(resFilePath), getFileByPath(zipFilePath), comment);
      }
  
      /**
       * 压缩文件
       *
       * @param resFile 待压缩文件
       * @param zipFile 压缩文件
       * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
       * @throws IOException IO 错误时抛出
       */
      public static boolean zipFile(File resFile, File zipFile) throws IOException {
          return zipFile(resFile, zipFile, null);
      }
  
      /**
       * 压缩文件
       *
       * @param resFile 待压缩文件
       * @param zipFile 压缩文件
       * @param comment 压缩文件的注释
       * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
       * @throws IOException IO 错误时抛出
       */
      public static boolean zipFile(File resFile, File zipFile, String comment) throws IOException {
          if (resFile == null || zipFile == null) return false;
          ZipOutputStream zos = null;
          try {
              zos = new ZipOutputStream(new FileOutputStream(zipFile));
              return zipFile(resFile, "", zos, comment);
          } finally {
              if (zos != null) {
                  zos.close();
              }
          }
      }
  
      /**
       * 压缩文件
       *
       * @param resFile  待压缩文件
       * @param rootPath 相对于压缩文件的路径
       * @param zos      压缩文件输出流
       * @param comment  压缩文件的注释
       * @return {@code true}: 压缩成功<br>{@code false}: 压缩失败
       * @throws IOException IO 错误时抛出
       */
      private static boolean zipFile(File resFile, String rootPath, ZipOutputStream zos, String comment) throws IOException {
          rootPath = rootPath + (TextUtils.isEmpty(rootPath) ? "" : File.separator) + resFile.getName();
          if (resFile.isDirectory()) {
              File[] fileList = resFile.listFiles();
              // 如果是空文件夹那么创建它，我把'/'换为 File.separator 测试就不成功，eggPain
              if (fileList == null || fileList.length <= 0) {
                  ZipEntry entry = new ZipEntry(rootPath + '/');
                  if (!TextUtils.isEmpty(comment)) entry.setComment(comment);
                  zos.putNextEntry(entry);
                  zos.closeEntry();
              } else {
                  for (File file : fileList) {
                      // 如果递归返回 false 则返回 false
                      if (!zipFile(file, rootPath, zos, comment)) return false;
                  }
              }
          } else {
              InputStream is = null;
              try {
                  is = new BufferedInputStream(new FileInputStream(resFile));
                  ZipEntry entry = new ZipEntry(rootPath);
                  if (!TextUtils.isEmpty(comment)) entry.setComment(comment);
                  zos.putNextEntry(entry);
                  byte buffer[] = new byte[BUFFER_LEN];
                  int len;
                  while ((len = is.read(buffer, 0, BUFFER_LEN)) != -1) {
                      zos.write(buffer, 0, len);
                  }
                  zos.closeEntry();
              } finally {
                  if (zos != null) {
                      zos.close();
                  }
              }
          }
          return true;
      }
  
      /**
       * 解压文件
       *
       * @param zipFilePath 待解压文件路径
       * @param destDirPath 目标目录路径
       * @return 文件链表
       * @throws IOException IO 错误时抛出
       */
      public static List<File> unzipFile(String zipFilePath, String destDirPath) throws IOException {
          return unzipFileByKeyword(zipFilePath, destDirPath, null);
      }
  
      /**
       * 解压文件
       *
       * @param zipFile 待解压文件
       * @param destDir 目标目录
       * @return 文件链表
       * @throws IOException IO 错误时抛出
       */
      public static List<File> unzipFile(File zipFile, File destDir) throws IOException {
          return unzipFileByKeyword(zipFile, destDir, null);
      }
  
      /**
       * 解压带有关键字的文件
       *
       * @param zipFilePath 待解压文件路径
       * @param destDirPath 目标目录路径
       * @param keyword     关键字
       * @return 返回带有关键字的文件链表
       * @throws IOException IO 错误时抛出
       */
      public static List<File> unzipFileByKeyword(String zipFilePath, String destDirPath, String keyword) throws IOException {
          return unzipFileByKeyword(getFileByPath(zipFilePath), getFileByPath(destDirPath), keyword);
      }
  
      /**
       * 解压带有关键字的文件
       *
       * @param zipFile 待解压文件
       * @param destDir 目标目录
       * @param keyword 关键字
       * @return 返回带有关键字的文件链表
       * @throws IOException IO 错误时抛出
       */
      public static List<File> unzipFileByKeyword(File zipFile, File destDir, String keyword) throws IOException {
          if (zipFile == null || destDir == null) return null;
          List<File> files = new ArrayList<>();
          ZipFile zf = new ZipFile(zipFile);
          Enumeration<?> entries = zf.entries();
          if (TextUtils.isEmpty(keyword)) {
              while (entries.hasMoreElements()) {
                  ZipEntry entry = ((ZipEntry) entries.nextElement());
                  String entryName = entry.getName();
                  if (!unzipChildFile(destDir, files, zf, entry, entryName)) return files;
              }
          } else {
              while (entries.hasMoreElements()) {
                  ZipEntry entry = ((ZipEntry) entries.nextElement());
                  String entryName = entry.getName();
                  if (entryName.contains(keyword)) {
                      if (!unzipChildFile(destDir, files, zf, entry, entryName)) return files;
                  }
              }
          }
          return files;
      }
  
      private static boolean unzipChildFile(File destDir, List<File> files, ZipFile zf, ZipEntry entry, String entryName) throws IOException {
          String filePath = destDir + File.separator + entryName;
          File file = new File(filePath);
          files.add(file);
          if (entry.isDirectory()) {
              if (!createOrExistsDir(file)) return false;
          } else {
              if (!createOrExistsFile(file)) return false;
              InputStream in = null;
              OutputStream out = null;
              try {
                  in = new BufferedInputStream(zf.getInputStream(entry));
                  out = new BufferedOutputStream(new FileOutputStream(file));
                  byte buffer[] = new byte[BUFFER_LEN];
                  int len;
                  while ((len = in.read(buffer)) != -1) {
                      out.write(buffer, 0, len);
                  }
              } finally {
                  if (in != null) in.close();
                  if (out != null) out.close();
              }
          }
          return true;
      }
  
      /**
       * 获取压缩文件中的文件路径链表
       *
       * @param zipFilePath 压缩文件路径
       * @return 压缩文件中的文件路径链表
       * @throws IOException IO 错误时抛出
       */
      public static List<String> getFilesPath(String zipFilePath) throws IOException {
          return getFilesPath(getFileByPath(zipFilePath));
      }
  
      /**
       * 获取压缩文件中的文件路径链表
       *
       * @param zipFile 压缩文件
       * @return 压缩文件中的文件路径链表
       * @throws IOException IO 错误时抛出
       */
      public static List<String> getFilesPath(File zipFile) throws IOException {
          if (zipFile == null) return null;
          List<String> paths = new ArrayList<>();
          Enumeration<?> entries = new ZipFile(zipFile).entries();
          while (entries.hasMoreElements()) {
              paths.add(((ZipEntry) entries.nextElement()).getName());
          }
          return paths;
      }
  
      /**
       * 获取压缩文件中的注释链表
       *
       * @param zipFilePath 压缩文件路径
       * @return 压缩文件中的注释链表
       * @throws IOException IO 错误时抛出
       */
      public static List<String> getComments(String zipFilePath) throws IOException {
          return getComments(getFileByPath(zipFilePath));
      }
  
      /**
       * 获取压缩文件中的注释链表
       *
       * @param zipFile 压缩文件
       * @return 压缩文件中的注释链表
       * @throws IOException IO 错误时抛出
       */
      public static List<String> getComments(File zipFile) throws IOException {
          if (zipFile == null) return null;
          List<String> comments = new ArrayList<>();
          Enumeration<?> entries = new ZipFile(zipFile).entries();
          while (entries.hasMoreElements()) {
              ZipEntry entry = ((ZipEntry) entries.nextElement());
              comments.add(entry.getComment());
          }
          return comments;
      }
  
      private static boolean createOrExistsDir(File file) {
          return file != null && (file.exists() ? file.isDirectory() : file.mkdirs());
      }
  
      private static boolean createOrExistsFile(File file) {
          if (file == null) return false;
          if (file.exists()) return file.isFile();
          if (!createOrExistsDir(file.getParentFile())) return false;
          try {
              return file.createNewFile();
          } catch (IOException e) {
              e.printStackTrace();
              return false;
          }
      }
  
      private static File getFileByPath(String filePath) {
          return TextUtils.isEmpty(filePath) ? null : new File(filePath);
      }
  }
  