ZipOutputStream无法为目录创建新条目:压缩文件仅生成首个目录的问题求助
嘿,我帮你揪出问题根源啦!你的压缩代码之所以只生成第一个目录,核心问题出在递归调用zip()方法时每次都重新创建了ZipOutputStream和FileOutputStream——这就相当于你每次处理一个子目录,就把之前的压缩文件删掉重写一遍,最后自然只剩最后一次写入的内容(看起来像是第一个目录,其实是递归链里最后被处理的那个,只是表现成只有第一个目录保留)。
问题拆解一下
你原来的代码逻辑是:每次遇到目录就调用zip(),而每次调用zip()都会重新打开test.zip文件并创建新的输出流,这直接覆盖了之前已经写入的压缩内容。等递归返回后,上层的输出流对应的文件已经被覆盖,后续的写入操作根本无效,所以最终压缩包里只剩孤零零的一个目录。
修复方案:共享同一个输出流
我们只需要把ZipOutputStream作为参数传递给递归方法,让整个压缩过程用同一个流写入,就不会出现覆盖问题了。给你改好的代码:
1. 重构后的压缩方法
// 这是对外调用的入口,只初始化一次输出流 private void startZip(String rootPath) { try { // 初始化输出流,只做一次 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream( new FileOutputStream("/sdcard/Android/data/com.kpwnapps.pmmpplugins/files/PocketMine-MP/test.zip")); ZipOutputStream zipOutputStream = new ZipOutputStream(bufferedOutputStream, StandardCharsets.UTF_8); // 从根目录开始递归压缩 zipDirectoryOrFile(new File(rootPath), "", zipOutputStream); // 最后统一关闭流 zipOutputStream.flush(); zipOutputStream.close(); bufferedOutputStream.close(); } catch (Exception e) { e.printStackTrace(); // 别再忽略异常啦,打印出来方便调试问题 } } // 核心递归方法,共享同一个ZipOutputStream private void zipDirectoryOrFile(File file, String parentPath, ZipOutputStream zipOutputStream) throws IOException { // 拼接当前条目在压缩包里的路径 String entryName = parentPath + file.getName(); if (file.isDirectory()) { // 添加目录条目,必须加末尾的斜杠,不然会被当成文件 zipOutputStream.putNextEntry(new ZipEntry(entryName + "/")); zipOutputStream.closeEntry(); // 目录条目不用写内容,直接关闭 // 递归处理子目录和文件 File[] children = file.listFiles(); if (children != null) { for (File child : children) { // 传递当前目录的路径作为子条目的父路径 zipDirectoryOrFile(child, entryName + "/", zipOutputStream); } } } else { // 处理普通文件 BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream(file)); zipOutputStream.putNextEntry(new ZipEntry(entryName)); // 用IOUtils拷贝文件内容到压缩流 IOUtils.copy(bufferedInputStream, zipOutputStream); // 关闭当前条目和输入流 zipOutputStream.closeEntry(); bufferedInputStream.close(); } } // 原来的listFiles方法可以保留,不过重构后其实用不上了 private Set<String> listFiles(String path){ File f = new File(path); Set<String> files = new HashSet<>(); for(File file : Objects.requireNonNull(f.listFiles())){ files.add(file.getPath()); } return files; }
2. 关键修改点
- 新增
startZip()作为入口,只初始化一次输出流,彻底避免重复创建覆盖文件。 - 递归方法
zipDirectoryOrFile()接收当前文件、父路径和共享的输出流,保证所有条目都写入同一个压缩包,目录结构也能正确保留。 - 处理目录时明确添加斜杠结尾的条目,避免被当成文件。
- 不再忽略异常,打印堆栈信息能帮你快速定位后续可能出现的问题。
3. 怎么调用?
原来你调用zip(listFiles(xxx))的地方,现在换成startZip(你的根目录路径)就行啦。
额外小提醒
- 你用的是App专属目录
/sdcard/Android/data/...,这个在Android 10+是不需要额外存储权限的,没问题。 - 如果处理超大文件,担心内存占用的话,可以自己实现分块拷贝,不过
IOUtils.copy()已经是高效的流处理方式了,一般够用。 - 记得压缩完成后要关闭所有流,上面的代码已经帮你处理了~
内容的提问来源于stack exchange,提问作者kpwn Apps
相关产品推荐
相关产品推荐

