如何通过SAF与DocumentFile正确解压含多级子目录的ZIP压缩包?
基于SAF实现ZIP解压的问题说明
场景背景
使用存储访问框架(SAF)解压ZIP压缩包时,受框架限制无法使用任何File类对象,只能依靠ZipInputStream、ZipOutputStream与DocumentFile完成全部解压逻辑。
本次需要处理的ZIP包内置目录结构如下:
Folder 1/ABC 001.jpg Folder 1/ABC 002.jpg Folder 2/ABC 003.jpg Folder 2/ABC 004.jpg Folder 2/Folder 3/ABC 005.jpg Folder 2/Folder 3/ABC 006.jpg Folder 2/Folder 3/Folder 4/ABC 007.jpg Folder 2/Folder 3/Folder 4/ABC 008.jpg ABC 009.jpg
现有实现代码
public void extractZipFile(DocumentFile srcZipFile, DocumentFile destDir) throws IOException { ZipEntry entry; InputStream inputStream = resolver.openInputStream(srcZipFile.getUri()); try (java.util.zip.ZipInputStream zipInputStream = new java.util.zip.ZipInputStream(inputStream)) { while ((entry = zipInputStream.getNextEntry()) != null) { DocumentFile currentDestDir = destDir; if (!entry.isDirectory()) { unzipFile(entry, zipInputStream, currentDestDir); } else { String finalFolderName = entry.getName().replace("/", ""); currentDestDir = destDir.createDirectory(finalFolderName); } } } inputStream.close(); } private void unzipFile(ZipEntry fileEntry, java.util.zip.ZipInputStream zipInputStream, DocumentFile destDir) throws IOException { int readLen; byte[] readBuffer = new byte[BUFFER_SIZE]; DocumentFile destFile = destDir.createFile("*/*", fileEntry.getName()); try (OutputStream outputStream = resolver.openOutputStream(destFile.getUri())) { while ((readLen = zipInputStream.read(readBuffer)) != -1) { outputStream.write(readBuffer, 0, readLen); } } }
现存问题与偏差原因
按照上述代码执行后,解压输出的目录结构完全不符合预期,核心问题有两点:
- 目录路径解析逻辑完全失效
- 每次遍历ZipEntry时,
currentDestDir都被重置为根目标目录,没有沿ZIP内置路径逐级定位嵌套目录层级 - 处理目录条目时,仅靠去掉字符串里的斜杠生成单层文件夹名,在根目录下创建独立文件夹,完全没有形成多层嵌套的目录结构
- 每次遍历ZipEntry时,
- 文件写入逻辑路径错位
- 处理普通文件条目时,直接将带全路径的
entry.getName()作为文件名传入创建文件的方法,没有拆分路径片段、定位到对应层级的目标目录,导致所有文件直接落在根目录下,甚至因字符串包含斜杠出现命名异常
- 处理普通文件条目时,直接将带全路径的
修正实现逻辑
不需要单独区分处理目录/文件条目,统一对每个ZipEntry的全路径做拆分,从根目标目录开始逐级匹配、创建层级目录,最后处理文件写入即可:
public void extractZipFile(DocumentFile srcZipFile, DocumentFile destDir) throws IOException { ZipEntry entry; InputStream inputStream = resolver.openInputStream(srcZipFile.getUri()); try (java.util.zip.ZipInputStream zipInputStream = new java.util.zip.ZipInputStream(inputStream)) { while ((entry = zipInputStream.getNextEntry()) != null) { // 拆分全路径为各级片段 String[] pathSegments = entry.getName().split("/"); DocumentFile currentDir = destDir; // 遍历除最后一段外的所有路径片段,逐级定位/创建目录 for (int i = 0; i < pathSegments.length - 1; i++) { String segment = pathSegments[i]; if (segment.isEmpty()) continue; DocumentFile targetDir = currentDir.findFile(segment); if (targetDir == null || !targetDir.isDirectory()) { targetDir = currentDir.createDirectory(segment); } currentDir = targetDir; } // 仅处理普通文件,跳过空的目录条目写入 if (!entry.isDirectory()) { String fileName = pathSegments[pathSegments.length - 1]; if (fileName.isEmpty()) continue; unzipFile(entry, zipInputStream, currentDir, fileName); } } } inputStream.close(); } private void unzipFile(ZipEntry fileEntry, java.util.zip.ZipInputStream zipInputStream, DocumentFile destDir, String fileName) throws IOException { int readLen; byte[] readBuffer = new byte[BUFFER_SIZE]; DocumentFile destFile = destDir.createFile("*/*", fileName); try (OutputStream outputStream = resolver.openOutputStream(destFile.getUri())) { while ((readLen = zipInputStream.read(readBuffer)) != -1) { outputStream.write(readBuffer, 0, readLen); } } }
内容的提问来源于stack exchange,提问作者Al_HK
相关产品推荐
相关产品推荐

