在 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 objects
的 Files[]
开始下一个目录时,我看到了这一点。我相信我失去了对旧 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 关闭时写入,所以如果你忽略关闭索引将会丢失:因此损坏。
另外请注意,您不需要同时关闭 fis
和 origin
。只需关闭 origin
... 它就会关闭 fis
。
更新 - 虽然您修复了最初的关闭错误,但还有更多:
您添加了一个 finally
块来关闭 out
。那是错的。您不希望 addDirToArchive
关闭 out
。这可能是您出现异常的原因。
完成此操作后会出现几个问题:
if (currentZipFileSize >= EMAIL_SIZE)
{
out.close();
out = new ZipOutputStream(new FileOutputStream(...));
currentZipFileSize = 0;
}
由于out
是局部参数,调用者看不到变化
你做。因此:
当你在调用者中调用 out.close()
时,你可能正在关闭
原来的 ZIP(已经关闭)...不是现在的
如果你打电话给 addDirToArchive(out, destinationDir, dirName)
多次,在随后的调用中,您可能会传递一个封闭的 ZIP 文件。
您的异常处理被误导了(IMO)。如果将文件写入 ZIP 时出现 I/O 错误,您不想记录消息并继续操作。你想摆脱困境。要么完全崩溃应用程序,要么停止做你正在做的事情。在这种情况下,您 "stream is closed" 显然是代码中的错误,您的异常处理实际上是在告诉应用程序忽略它。
一些建议:
如果您将打开和关闭资源的责任分摊到多个方法中,您需要非常注意哪些代码负责关闭哪些内容。你需要明白你在做什么。
盲目应用(所谓的)"solutions"(就像 finally
的东西)...因为有人说 "XXX is best practice" 或 "always do XXX"。 .. 会给你带来麻烦。您需要 1) 了解 "solution" 的作用,以及 2) 思考 解决方案是否真正满足您的需求。
我正在尝试将目录从一个区域 (sdCard/someFolder)
压缩到第二个目录 (sdCard/Download)
,直到 .zip 文件大小变为 5mb。然后,我想创建一个新的 .zip
文件,将新文件填充到 5mb,等等
目前,我的代码成功地将文件压缩到 .zip 目录中,但其中一个 .zip 目录总是损坏。当我的 for 循环退出 22 objects
的第一个 Files[]
并以 4 objects
的 Files[]
开始下一个目录时,我看到了这一点。我相信我失去了对旧 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 关闭时写入,所以如果你忽略关闭索引将会丢失:因此损坏。
另外请注意,您不需要同时关闭 fis
和 origin
。只需关闭 origin
... 它就会关闭 fis
。
更新 - 虽然您修复了最初的关闭错误,但还有更多:
您添加了一个
finally
块来关闭out
。那是错的。您不希望addDirToArchive
关闭out
。这可能是您出现异常的原因。完成此操作后会出现几个问题:
if (currentZipFileSize >= EMAIL_SIZE) { out.close(); out = new ZipOutputStream(new FileOutputStream(...)); currentZipFileSize = 0; }
由于
out
是局部参数,调用者看不到变化 你做。因此:当你在调用者中调用
out.close()
时,你可能正在关闭 原来的 ZIP(已经关闭)...不是现在的如果你打电话给
addDirToArchive(out, destinationDir, dirName)
多次,在随后的调用中,您可能会传递一个封闭的 ZIP 文件。
您的异常处理被误导了(IMO)。如果将文件写入 ZIP 时出现 I/O 错误,您不想记录消息并继续操作。你想摆脱困境。要么完全崩溃应用程序,要么停止做你正在做的事情。在这种情况下,您 "stream is closed" 显然是代码中的错误,您的异常处理实际上是在告诉应用程序忽略它。
一些建议:
如果您将打开和关闭资源的责任分摊到多个方法中,您需要非常注意哪些代码负责关闭哪些内容。你需要明白你在做什么。
盲目应用(所谓的)"solutions"(就像
finally
的东西)...因为有人说 "XXX is best practice" 或 "always do XXX"。 .. 会给你带来麻烦。您需要 1) 了解 "solution" 的作用,以及 2) 思考 解决方案是否真正满足您的需求。