在 Android 中使用 ZipOutputStream 将最多 24mb 的文件压缩成 .zip

Compress up to 24mb files into .zip using ZipOutputStream in Android

我正在尝试将目录从一个区域 (sdCard/someFolder) 压缩到第二个目录 (sdCard/Download),直到 .zip 文件大小变为 5mb。然后,我想创建一个新的 .zip 文件,将新文件填充到 5mb,等等

目前,我的代码成功地将文件压缩到 .zip 目录中,但其中一个 .zip 目录总是损坏。当我的 for 循环退出 22 objects 的第一个 Files[] 并以 4 objectsFiles[] 开始下一个目录时,我看到了这一点。我相信我失去了对旧 OutputStreams 的一些清理。 out.putNextEntry() 在 for 循环的第二次尝试后变为 null。任何帮助就足够了。

private static void addDirToArchive(ZipOutputStream out, FileOutputStream destinationDir, File sdCardMNDLogs)
{
    File[] listOfFiles = sdCardMNDLogs.listFiles();

    BufferedInputStream origin = null;

    Log.i(TAG3, "Reading directory: " + sdCardMNDLogs.getName());

    try{

    byte[] buffer = new byte[BUFFER];
    for(int i = 0; i < listOfFiles.length; i++)
    {
        if(listOfFiles[i].isDirectory())
        {
            addDirToArchive(out, destinationDir, listOfFiles[i]);
            continue;
        }
        try 
        {
            FileInputStream fis = new FileInputStream(listOfFiles[i]);
            origin = new BufferedInputStream(fis,BUFFER);
            ZipEntry ze = new ZipEntry(listOfFiles[i].getName());

            if(currentZipFileSize >= EMAIL_SIZE)
            {
                out.close();
                Log.d(emailTAG, "Creating new zipfile: /Download/MND/nwdLogs_" + i);
                out = new ZipOutputStream(new FileOutputStream(new File(sdCard.getAbsolutePath() + "/Download/MND/nwdLogs_ " + i + ".zip")));
                currentZipFileSize = 0;
            }
            out.putNextEntry(ze);
            int length;
            Log.i(TAG3, "Adding file: " + listOfFiles[i].getName());
            while((length = origin.read(buffer, 0, BUFFER)) != -1)
            {
                out.write(buffer, 0, length);
            }
            out.closeEntry();
            origin.close();
            currentZipFileSize = currentZipFileSize + ze.getCompressedSize();
        }
        catch(IOException ioe)
        {
            Log.e(TAG3, "IOException: " + ioe);
        }
    }
    }
    finally
    {
        try {
            out.close();
    } catch (IOException e) 
    {
        e.printStackTrace();
    }
}

}

FileOutputStream destinationDir = new FileOutputStream(sdCard.getAbsolutePath() + "/Download/Dir/nwdLogs.zip");
ZipOutputStream out = new ZipOutputStream(destinationDir);

currentZipFileSize = 0;
addDirToArchive(out, destinationDir, dirName);
out.close();
destinationDir.close();

我怀疑问题是您在打开下一个 ZIP 文件之前没有调用 out.close()。我的理解是 ZIP 的索引只在 ZIP 关闭时写入,所以如果你忽略关闭索引将会丢失:因此损坏。

另外请注意,您不需要同时关闭 fisorigin。只需关闭 origin ... 它就会关闭 fis


更新 - 虽然您修复了最初的关闭错误,但还有更多:

  1. 您添加了一个 finally 块来关闭 out。那是错的。您不希望 addDirToArchive 关闭 out。这可能是您出现异常的原因。

  2. 完成此操作后会出现几个问题:

    if (currentZipFileSize >= EMAIL_SIZE)
        {
            out.close();
            out = new ZipOutputStream(new FileOutputStream(...));
            currentZipFileSize = 0;
        }
    

    由于out是局部参数,调用者看不到变化 你做。因此:

    • 当你在调用者中调用 out.close() 时,你可能正在关闭 原来的 ZIP(已经关闭)...不是现在的

    • 如果你打电话给 addDirToArchive(out, destinationDir, dirName) 多次,在随后的调用中,您可能会传递一个封闭的 ZIP 文件。

  3. 您的异常处理被误导了(IMO)。如果将文件写入 ZIP 时出现 I/O 错误,您不想记录消息并继续操作。你想摆脱困境。要么完全崩溃应用程序,要么停止做你正在做的事情。在这种情况下,您 "stream is closed" 显然是代码中的错误,您的异常处理实际上是在告诉应用程序忽略它。

一些建议:

  • 如果您将打开和关闭资源的责任分摊到多个方法中,您需要非常注意哪些代码负责关闭哪些内容。你需要明白你在做什么。

  • 盲目应用(所谓的)"solutions"(就像 finally 的东西)...因为有人说 "XXX is best practice" 或 "always do XXX"。 .. 会给你带来麻烦。您需要 1) 了解 "solution" 的作用,以及 2) 思考 解决方案是否真正满足您的需求。