ZipOutputStream 在 Android 上生成损坏的 zip 文件
Posted
技术标签:
【中文标题】ZipOutputStream 在 Android 上生成损坏的 zip 文件【英文标题】:ZipOutputStream produces corrupted zip file on Android 【发布时间】:2014-08-31 18:05:58 【问题描述】:我已经使用 zip 存档从应用程序备份用户数据,我正在将数据库和共享首选项文件复制到 zip 存档并计算输入文件的 MD5 校验和以防止用户修改备份数据。
要从存档中恢复,我将备份文件解压缩到临时目录,检查校验和,然后将首选项\数据库文件复制到相应的文件夹中。
我的一些用户抱怨应用程序生成损坏的备份文件(zip 文件确实已损坏)。
这是将所有文件压缩为 zip 文件的代码:
public void backup(String filename)
File file = new File(getBackupDirectory(), filename);
FileOutputStream fileOutputStream = null;
ZipOutputStream stream = null;
try
String settingsMD5 = null;
String databaseMD5 = null;
if (file.exists())
file.delete();
fileOutputStream = new FileOutputStream(file);
stream = new ZipOutputStream(new BufferedOutputStream(fileOutputStream));
File database = getDatabasePath(databaseFileName);
File dataDirectory = getFilesDir();
if (dataDirectory != null)
File settings = new File(dataDirectory.getParentFile(), "/shared_prefs/" + PREFERENCES_FILENAME);
settingsMD5 = zipFile("preferences", stream, settings);
databaseMD5 = zipFile("database.db", stream, database);
JSONObject jsonObject = new JSONObject();
try
jsonObject.put(META_DATE, new SimpleDateFormat(DATE_FORMAT, Locale.US).format(new Date()));
jsonObject.put(META_DATABASE, databaseMD5);
jsonObject.put(META_SHARED_PREFS, settingsMD5);
catch (Exception e)
e.printStackTrace();
InputStream metadata = new ByteArrayInputStream(jsonObject.toString().getBytes("UTF-8"));
zipInputStream(stream, metadata, new ZipEntry("metadata"));
stream.finish();
stream.close();
stream = null;
return file;
catch (FileNotFoundException e)
//handling errrors
catch (IOException e)
//handling errrors
private String zipFile(String name, ZipOutputStream zipStream, File file) throws FileNotFoundException, IOException
ZipEntry zipEntry = new ZipEntry(name);
return zipInputStream(zipStream, new FileInputStream(file), zipEntry);
private String zipInputStream(ZipOutputStream zipStream, InputStream fileInputStream, ZipEntry zipEntry) throws IOException
InputStream inputStream = new BufferedInputStream(fileInputStream);
MessageDigest messageDigest = null;
try
messageDigest = MessageDigest.getInstance("MD5");
if (messageDigest != null)
inputStream = new DigestInputStream(inputStream, messageDigest);
catch (NoSuchAlgorithmException e)
zipStream.putNextEntry(zipEntry);
inputToOutput(inputStream, zipStream);
zipStream.closeEntry();
inputStream.close();
if (messageDigest != null)
return getDigestString(messageDigest.digest());
return null;
private String getDigestString(byte[] digest)
StringBuffer hexString = new StringBuffer();
for (int i = 0; i < digest.length; i++)
String hex = Integer.toHexString(0xFF & digest[i]);
if (hex.length() == 1)
hex = new StringBuilder("0").append(hex).toString();
hexString.append(hex);
return hexString.toString();
private void inputToOutput(InputStream inputStream, OutputStream outputStream) throws IOException
byte[] buffer = new byte[BUFFER];
int count = 0;
while ((count = inputStream.read(buffer, 0, BUFFER)) != -1)
outputStream.write(buffer, 0, count);
【问题讨论】:
我猜你应该在关闭流之前调用stream.flush();
。
我添加了 stream.flush();在 stream.finish() 之后 - 有时仍会收到损坏的 zip 文件。
是否有可能获取此类损坏的 zip 文件的示例?
【参考方案1】:
您可以考虑使用 zip4j 库。我通过使用这个库解决了我遇到的问题(相同的问题 - 不同的方向)。一些 zip 文件无法使用原生 android 实现解码,但使用 zip4j。您也可以通过使用 zip4j 进行压缩来解决您的问题。
【讨论】:
谢谢,我试试看【参考方案2】:这里有一些代码仅使用 java 标准类将目录压缩到文件中。有了这个你可以打电话:
ZipUtils.zip(sourceDirectory, targetFile);
ZipUtils.unzip(sourceFile, targetDirectory);
代码:
package com.my.project.utils.zip;
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipUtils
public static void unzip(Path sourceFile, Path targetPath) throws IOException
try (ZipInputStream zipInStream = new ZipInputStream(Files.newInputStream(sourceFile)))
byte[] buffer = new byte[1024];
Files.createDirectories(targetPath);
ZipEntry entry = null;
while ((entry = zipInStream.getNextEntry()) != null)
Path entryPath = targetPath.resolve(entry.getName());
Files.createDirectories(entryPath.getParent());
Files.copy(zipInStream, entryPath);
zipInStream.closeEntry();
public static void zip(Path sourcePath, Path targetFile) throws IOException
try (ZipOutputStream zipOutStream = new ZipOutputStream(Files.newOutputStream(targetFile)))
if (Files.isDirectory(sourcePath))
zipDirectory(zipOutStream, sourcePath);
else
createZipEntry(zipOutStream, sourcePath, sourcePath);
private static void zipDirectory(ZipOutputStream zip, Path source) throws IOException
Files.walkFileTree(source, new SimpleFileVisitor<Path>()
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
return FileVisitResult.CONTINUE;
@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes attrs) throws IOException
createZipEntry(zip, source, path);
return FileVisitResult.CONTINUE;
);
private static void createZipEntry(ZipOutputStream zip, Path sourcePath, Path path) throws IOException
ZipEntry entry = new ZipEntry(sourcePath.relativize(path).toString());
zip.putNextEntry(entry);
Files.copy(path,zip);
zip.closeEntry();
【讨论】:
以上是关于ZipOutputStream 在 Android 上生成损坏的 zip 文件的主要内容,如果未能解决你的问题,请参考以下文章
使用 Java ZipOutputStream 和 BufferedOutputStream 的首选方式
如何在 Spring MVC 中将字节数组转换为 ZipOutputStream?
ZipOutputStream 类的 closeEntry()
使用 ZipOutputStream 和 FileStream 压缩文件时出现异常