ZipOutputStream creates corrupted zip file on Android

I have implemented backing up user data from an application using a zip archive, I copy the database and general settings files to the zip archive and calculate the MD5 checksum of the input files to prevent the user from modifying the backup data.

To restore from archive, I unzip the backup file to a temporary directory, check the checksums, and then copy the preferences \ database file to the appropriate folders.

Some of my users are complaining that the app creates corrupted backup files (the zip files are indeed corrupted).

Here is the code that compresses all files into a zip file:

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);
    }
}

      

+3


source to share


2 answers


You can use zip4j lib. I solved the problem I was facing (same problem - different direction) using this library. Some zip files that are not decoded with the native Android implementation, but with zip4j. You can also solve your problem using zip4j for compression.



+2


source


Here is some code to zip a directory to a file using only the standard java classes. With this, you can simply call:

ZipUtils.zip(sourceDirectory, targetFile);
ZipUtils.unzip(sourceFile, targetDirectory);

      



code:

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();
    }
}

      

+1


source







All Articles