You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 18:57:44