【问题标题】:zip/compress a folder full of files on android在android上压缩/压缩一个充满文件的文件夹
【发布时间】:2011-10-04 17:52:48
【问题描述】:

我需要压缩一个“项目”文件夹以允许用户通过电子邮件共享项目。我找到了一个将多个文件压缩到一个 zip 中的类,但我需要将文件夹结构保留在我的 zip 中。有没有办法在android上实现这一点?提前致谢。

【问题讨论】:

  • 澄清一下,你是指Android开发项目还是app中开发的项目?
  • 在应用程序中开发的项目,对此感到抱歉。另外我认为这就是我需要的:stackoverflow.com/questions/1399126/… 但是当我复制它时,我尽了最大努力,但找不到该行的方法: Deque queue = new LinkedList();去工作。我知道 Deque 是一个接口,而 LinkedList 实现了它,但是 eclipes 总是给我错误。
  • 没关系,找了好久才知道怎么做:crazysquirrel.com/computing/java/basics/…

标签: android zip directory compression


【解决方案1】:

这段代码应该可以解决问题。

注意:您必须通过将 WRITE_EXTERNAL_STORAGE 权限添加到 manifest.xml 文件来为您的应用添加文件写入权限。

/*
 * 
 * Zips a file at a location and places the resulting zip file at the toLocation
 * Example: zipFileAtPath("downloads/myfolder", "downloads/myFolder.zip");
 */

public boolean zipFileAtPath(String sourcePath, String toLocation) {
    final int BUFFER = 2048;

    File sourceFile = new File(sourcePath);
    try {
        BufferedInputStream origin = null;
        FileOutputStream dest = new FileOutputStream(toLocation);
        ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(
                dest));
        if (sourceFile.isDirectory()) {
            zipSubFolder(out, sourceFile, sourceFile.getParent().length());
        } else {
            byte data[] = new byte[BUFFER];
            FileInputStream fi = new FileInputStream(sourcePath);
            origin = new BufferedInputStream(fi, BUFFER);
            ZipEntry entry = new ZipEntry(getLastPathComponent(sourcePath));
            entry.setTime(sourceFile.lastModified()); // to keep modification time after unzipping
            out.putNextEntry(entry);
            int count;
            while ((count = origin.read(data, 0, BUFFER)) != -1) {
                out.write(data, 0, count);
            }
        }
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
        return false;
    }
    return true;
}

/*
 * 
 * Zips a subfolder
 * 
 */

private void zipSubFolder(ZipOutputStream out, File folder,
        int basePathLength) throws IOException {

    final int BUFFER = 2048;

    File[] fileList = folder.listFiles();
    BufferedInputStream origin = null;
    for (File file : fileList) {
        if (file.isDirectory()) {
            zipSubFolder(out, file, basePathLength);
        } else {
            byte data[] = new byte[BUFFER];
            String unmodifiedFilePath = file.getPath();
            String relativePath = unmodifiedFilePath
                    .substring(basePathLength);
            FileInputStream fi = new FileInputStream(unmodifiedFilePath);
            origin = new BufferedInputStream(fi, BUFFER);
            ZipEntry entry = new ZipEntry(relativePath);
            entry.setTime(file.lastModified()); // to keep modification time after unzipping
            out.putNextEntry(entry);
            int count;
            while ((count = origin.read(data, 0, BUFFER)) != -1) {
                out.write(data, 0, count);
            }
            origin.close();
        }
    }
}

/*
 * gets the last path component
 * 
 * Example: getLastPathComponent("downloads/example/fileToZip");
 * Result: "fileToZip"
 */
public String getLastPathComponent(String filePath) {
    String[] segments = filePath.split("/");
    if (segments.length == 0)
        return "";
    String lastPathComponent = segments[segments.length - 1];
    return lastPathComponent;
}

【讨论】:

  • 嘿@HailZeon!很棒的代码,真的很有帮助。 getLastPathComponent(sourcePath) 做什么以及它是如何定义的?谢谢!
  • 嘿@RaymondMachira。我添加了 getLastPathComponent 的定义。它基本上采用路径(“folder1/subfolder/example.txt”)并返回“folder1/subfolder/”。 zip 文件中可能没有定义包含文件夹,因此我们需要删除子路径并添加它。
  • 我在 zip 文件中得到了空的命名文件夹,解决方法是将 sourceFile.getParent().length() 替换为 sourceFile.getParent().length()+1。
  • 你应该用 filePath.substring(filePath.lastIndexOf("/")) 替换 getLastPathComponent(String filePath) 内容
  • 很棒的代码——我正在为我发现的一个错误添加一个修复程序——当尝试在 Windows 上解压缩时,由于解析给 ZipEntry 对象的相对路径上的第一个“/”问题而失败。 ===== 代码修复 ==== String relativePath = unmodifiedFilePath .substring(basePathLength+1);
【解决方案2】:

这就是我的做法:

private static void zipFolder(String inputFolderPath, String outZipPath) {
    try {
        FileOutputStream fos = new FileOutputStream(outZipPath);
        ZipOutputStream zos = new ZipOutputStream(fos);
        File srcFile = new File(inputFolderPath);
        File[] files = srcFile.listFiles();
        Log.d("", "Zip directory: " + srcFile.getName());
        for (int i = 0; i < files.length; i++) {
            Log.d("", "Adding file: " + files[i].getName());
            byte[] buffer = new byte[1024];
            FileInputStream fis = new FileInputStream(files[i]);
            zos.putNextEntry(new ZipEntry(files[i].getName()));
            int length;
            while ((length = fis.read(buffer)) > 0) {
                zos.write(buffer, 0, length);
            }
            zos.closeEntry();
            fis.close();
        }
        zos.close();
    } catch (IOException ioe) {
        Log.e("", ioe.getMessage());
    }
}

【讨论】:

  • 如果源文件夹包含另一个文件夹,则无法正常工作
  • 压缩给定的文件夹。我压缩了一个充满图像的文件夹,给定的文件夹被压缩了,但是解压缩时文件夹中的图像不可读。怎么可能解决?
  • 如果要解压,只需在文件夹名称末尾添加扩展名.zip,即可轻松解压。
【解决方案3】:

我已经修改了来自 HailZeon 的代码以在 Windows 下正常工作。 Zip 条目必须在启动新条目之前关闭,并且条目名称(如“/file.txt”)处的起始“/”也会产生问题

/**
 * Zips a Folder to "[Folder].zip"
 * @param toZipFolder Folder to be zipped
 * @return the resulting ZipFile
 */
public static File zipFolder(File toZipFolder) {
    File ZipFile = new File(toZipFolder.getParent(), format("%s.zip", toZipFolder.getName()));
    try {
        ZipOutputStream out = new ZipOutputStream(new FileOutputStream(ZipFile));
        zipSubFolder(out, toZipFolder, toZipFolder.getPath().length());
        out.close();
        return ZipFile;
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    }
}

/**
 * Main zip Function
 * @param out Target ZipStream
 * @param folder Folder to be zipped
 * @param basePathLength Length of original Folder Path (for recursion)
 */
private static void zipSubFolder(ZipOutputStream out, File folder, int basePathLength) throws IOException {

    final int BUFFER = 2048;

    File[] fileList = folder.listFiles();
    BufferedInputStream origin = null;
    for (File file : fileList) {
        if (file.isDirectory()) {
            zipSubFolder(out, file, basePathLength);
        } else {
            byte data[] = new byte[BUFFER];

            String unmodifiedFilePath = file.getPath();
            String relativePath = unmodifiedFilePath.substring(basePathLength + 1);

            FileInputStream fi = new FileInputStream(unmodifiedFilePath);
            origin = new BufferedInputStream(fi, BUFFER);

            ZipEntry entry = new ZipEntry(relativePath);
            entry.setTime(file.lastModified()); // to keep modification time after unzipping
            out.putNextEntry(entry);

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

【讨论】:

    【解决方案4】:

    使用来自location 的 zip4j 库。将 jar 文件导入您的 "app/libs/" 文件夹。并使用以下代码压缩您的目录/文件...

    try {
        File input = new File("path/to/your/input/fileOrFolder");
        String destinationPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "zippedItem.zip";
        ZipParameters parameters = new ZipParameters();
        parameters.setCompressionMethod(Zip4jConstants.COMP_STORE);
        parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
        File output = new File(destinationPath);
        ZipFile zipFile = new ZipFile(output);
        // .addFolder or .addFile depending on your input
        if (sourceFile.isDirectory())
            zipFile.addFolder(input, parameters);
        else
            zipFile.addFile(input, parameters);
        // Your input file/directory has been zipped at this point and you
        // can access it as a normal file using the following line of code
        File zippedFile = zipFile.getFile();
    } catch (ZipException e) {
        Log.e(TAG, Log.getStackTraceString(e));
    }
    

    【讨论】:

      【解决方案5】:

      如果您使用 java.util.zip 对象,那么您可以编写不修改目录结构的脚本。

      【讨论】:

        【解决方案6】:
        public static boolean zip(File sourceFile, File zipFile) {
            List<File> fileList = getSubFiles(sourceFile, true);
            ZipOutputStream zipOutputStream = null;
            try {
                zipOutputStream = new ZipOutputStream(new FileOutputStream(zipFile));
                int bufferSize = 1024;
                byte[] buf = new byte[bufferSize];
                ZipEntry zipEntry;
                for(int i = 0; i < fileList.size(); i++) {
                    File file = fileList.get(i);
                    zipEntry = new ZipEntry(sourceFile.toURI().relativize(file.toURI()).getPath());
                    zipOutputStream.putNextEntry(zipEntry);
                    if (!file.isDirectory()) {
                        InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
                        int readLength;
                        while ((readLength = inputStream.read(buf, 0, bufferSize)) != -1) {
                            zipOutputStream.write(buf, 0, readLength);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                return false;
            } finally {
                IoUtils.closeOS(zipOutputStream);
            }
            return true;
        }    
        
        public static List<File> getSubFiles(File baseDir, boolean isContainFolder) {
            List<File> fileList = new ArrayList<>();
            File[] tmpList = baseDir.listFiles();
            for (File file : tmpList) {
                if (file.isFile()) {
                    fileList.add(file);
                }
                if (file.isDirectory()) {
                    if (isContainFolder) {
                        fileList.add(file); //key code
                    }
                    fileList.addAll(getSubFiles(file));
                }
            }
            return fileList;
        }
        

        【讨论】:

          【解决方案7】:

          只需将@HailZeon 的答案转换为 Kotlin:

          import java.io.BufferedInputStream
          import java.io.File
          import java.io.FileInputStream
          import java.io.FileOutputStream
          import java.io.IOException
          import java.util.zip.ZipEntry
          import java.util.zip.ZipOutputStream
          import java.util.zip.ZipException
          
          private const val BUFFER = 2048
          
          /**
           * Compresses a file into a zip file
           * @author Arnau Mora
           * @since 20210318
           * @param file The source file
           * @param target The target file
           *
           * @throws NullPointerException If the entry name is null
           * @throws IllegalArgumentException If the entry name is longer than 0xFFFF byte
           * @throws SecurityException If a security manager exists and its SecurityManager.checkRead(String)
           * method denies read access to the file
           * @throws ZipException If a ZIP format error has occurred
           * @throws IOException If an I/O error has occurre
           */
          @Throws(
              NullPointerException::class,
              IllegalArgumentException::class,
              SecurityException::class,
              ZipException::class,
              IOException::class
          )
          fun zipFile(file: File, target: File) {
              val origin: BufferedInputStream
              val dest: FileOutputStream
              var zipOutput: ZipOutputStream? = null
              try {
                  dest = target.outputStream()
                  zipOutput = ZipOutputStream(dest.buffered(BUFFER))
                  if (file.isDirectory)
                      zipSubFolder(zipOutput, file, file.parent!!.length)
                  else {
                      val data = ByteArray(BUFFER)
                      val fi = file.inputStream()
                      origin = fi.buffered(BUFFER)
                      val entry = ZipEntry(getLastPathComponent(file.path))
                      entry.time = file.lastModified()
                      zipOutput.putNextEntry(entry)
                      var count = origin.read(data, 0, BUFFER)
                      while (count != -1) {
                          zipOutput.write(data, 0, count)
                          count = origin.read(data, 0, BUFFER)
                      }
                  }
              } finally {
                  zipOutput?.close()
              }
          }
          
          private fun zipSubFolder(zipOutput: ZipOutputStream, folder: File, basePathLength: Int) {
              val files = folder.listFiles() ?: return
              var origin: BufferedInputStream? = null
              try {
                  for (file in files) {
                      if (file.isDirectory)
                          zipSubFolder(zipOutput, folder, basePathLength)
                      else {
                          val data = ByteArray(BUFFER)
                          val unmodifiedFilePath = file.path
                          val relativePath = unmodifiedFilePath.substring(basePathLength)
                          val fi = FileInputStream(unmodifiedFilePath)
                          origin = fi.buffered(BUFFER)
                          val entry = ZipEntry(relativePath)
                          entry.time = file.lastModified()
                          zipOutput.putNextEntry(entry)
                          var count = origin.read(data, 0, BUFFER)
                          while (count != -1) {
                              zipOutput.write(data, 0, count)
                              count = origin.read(data, 0, BUFFER)
                          }
                      }
                  }
              } finally {
                  origin?.close()
              }
          }
          
          /*
           * gets the last path component
           *
           * Example: getLastPathComponent("downloads/example/fileToZip");
           * Result: "fileToZip"
           */
          private fun getLastPathComponent(filePath: String): String {
              val segments = filePath.split("/").toTypedArray()
              return if (segments.isEmpty()) "" else segments[segments.size - 1]
          }
          

          【讨论】:

            【解决方案8】:

            如果有人来到这里试图找到 @HailZeon 代码的 java8 更清洁的重构版本。这里是:

                private static final int BUFFER_SIZE = 2048;
            
                public File zip(File source, String zipFileName) throws IOException {
                    File zipFile = null;
                    if(source != null) {
                        File zipFileDestination = new File(getCacheDir(), zipFileName);
                        if (zipFileDestination.exists()) {
                            zipFileDestination.delete();
                        }
                        zipFile = zip(source, zipFileDestination);
                    }
                    return zipFile;
                }
            
                public File zip(File source, File zipFile) throws IOException {
                    int relativeStartingPathIndex = zipFile.getAbsolutePath().lastIndexOf("/") + 1;
                    if (source != null && zipFile != null) {
                        try (ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream( new FileOutputStream(zipFile)))) {
                            if (source.isDirectory()) {
                                zipSubDir(out, source, relativeStartingPathIndex);
                            } else {
                                try (BufferedInputStream origin = new BufferedInputStream(new FileInputStream(source))) {
                                    zipEntryFile(origin, out, source, relativeStartingPathIndex);
                                }
                            }
                        }
                    }
            
                    return zipFile;
                }
            
                private void zipSubDir(ZipOutputStream out, File dir, int relativeStartingPathIndex) throws IOException {
            
                    File[] files = dir.listFiles();
                    if (files != null) {
            
                        for(File file : files) {
                            if(file.isDirectory()) {
                                zipSubDir(out, file, relativeStartingPathIndex);
                            } else {
                                try (BufferedInputStream origin = new BufferedInputStream(new FileInputStream(file))) {
                                    zipEntryFile(origin, out, file, relativeStartingPathIndex);
                                }
                            }
                        }
            
                    }
                }
            
                private void zipEntryFile(BufferedInputStream origin, ZipOutputStream out, File file, int relativeStartingPathIndex) throws IOException {
                    String relativePath = file.getAbsolutePath().substring(relativeStartingPathIndex);
                    ZipEntry entry = new ZipEntry(relativePath);
                    entry.setTime(file.lastModified()); // to keep modification time after unzipping
                    out.putNextEntry(entry);
                    byte[] data = new byte[BUFFER_SIZE];
                    int count;
                    while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
                        out.write(data, 0, count);
                    }
                }
            

            【讨论】:

              猜你喜欢
              • 2010-09-05
              • 2013-03-09
              • 2012-08-07
              • 2010-12-14
              • 1970-01-01
              • 2013-10-30
              • 2012-06-19
              • 2012-01-14
              • 1970-01-01
              相关资源
              最近更新 更多