Unable to extract file from ZIP archive created on Android (device / OS dependent)

I am creating an archive on Android using the following code:

OutputStream os = new FileOutputStream(zipFile); ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(os)); try { zos.setLevel(8); byte[] buffer = new byte[32768]; for (VFile src : toPack) { ZipEntry entry = new ZipEntry(src.name); zos.putNextEntry(entry); src.pushToStream(zos, buffer); src.close(); zos.closeEntry(); } } finally { zos.close(); } 

I found that only one compression method is available - DEFLATED (only the STORED option is available there). This means that the archive is always compressed in one way.

If I run this code on Android 2.3.4 - I can unzip files on Windows using 7Zip; if I run it on Android 3 (or Samsung Galaxy Tab, not sure which one is wrong) - 7Zip shows the list of archives, but cannot unzip the file using the Unsupported compression method. At the same time, 7Zip shows Deflate as a compression method in a file, which means that it can handle it correctly.

Has anyone had this problem?

Thanks.

UDP: found another topic with a similar problem (maybe not all the same).

+4
source share
2 answers

@ User1269737 answer is correct; nearly. But it works only for single-file archives. Below is the code that analyzes the entire archive.

 /** * Replace wrong local file header byte * http://sourceforge.net/tracker/?func=detail&aid=3477810&group_id=14481&atid=114481 * Applies to Android API 9-13 * @param zip file * @throws IOException */ private static void fixInvalidZipFile(File zip) throws IOException { RandomAccessFile r = new RandomAccessFile(zip, "rw"); try { long eocd_offset = findEOCDRecord(r); if (eocd_offset > 0) { r.seek(eocd_offset + 16); // offset of first CDE in EOCD long cde_offset = readInt(r); // read offset of first Central Directory Entry long lfh_offset = 0; long fskip, dskip; while (true) { r.seek(cde_offset); if (readInt(r) != CDE_SIGNATURE) // got off sync! return; r.seek(cde_offset + 20); // compressed file size offset fskip = readInt(r); // fix the header // r.seek(lfh_offset + 7); short localFlagsHi = r.readByte(); // hi-order byte of local header flags (general purpose) r.seek(cde_offset + 9); short realFlagsHi = r.readByte(); // hi-order byte of central directory flags (general purpose) if (localFlagsHi != realFlagsHi) { // in latest versions this bug is fixed, so we're checking is bug exists. r.seek(lfh_offset + 7); r.write(realFlagsHi); } // calculate offset of next Central Directory Entry // r.seek(cde_offset + 28); // offset of variable CDE parts length in CDE dskip = 46; // length of fixed CDE part dskip += readShort(r); // file name dskip += readShort(r); // extra field dskip += readShort(r); // file comment cde_offset += dskip; if (cde_offset >= eocd_offset) // finished! break; // calculate offset of next Local File Header // r.seek(lfh_offset + 26); // offset of variable LFH parts length in LFH fskip += readShort(r); // file name fskip += readShort(r); // extra field fskip += 30; // length of fixed LFH part fskip += 16; // length of Data Descriptor (written after file data) lfh_offset += fskip; } } } finally { r.close(); } } //http://www.pkware.com/documents/casestudies/APPNOTE.TXT private static final int LFH_SIGNATURE = 0x04034b50; private static final int DD_SIGNATURE = 0x08074b50; private static final int CDE_SIGNATURE = 0x02014b50; private static final int EOCD_SIGNATURE = 0x06054b50; /** Find an offset of End Of Central Directory record in file */ private static long findEOCDRecord(RandomAccessFile f) throws IOException { long result = f.length() - 22; // 22 is minimal EOCD record length while (result > 0) { f.seek(result); if (readInt(f) == EOCD_SIGNATURE) return result; result--; } return -1; } /** Read a 4-byte integer from file converting endianness. */ private static int readInt(RandomAccessFile f) throws IOException { int result = 0; result |= f.read(); result |= (f.read() << 8); result |= (f.read() << 16); result |= (f.read() << 24); return result; } /** Read a 2-byte integer from file converting endianness. */ private static short readShort(RandomAccessFile f) throws IOException { short result = 0; result |= f.read(); result |= (f.read() << 8); return result; } 
+3
source

Need to update. It commits a file with one file. You should look at the following sequence in the zip file {0, 0x08, 0x08, 0x08, 0} and replace it with {0, 0x08, 0x00, 0x08, 0}

 /** * Replace wrong byte http://sourceforge.net/tracker/?func=detail&aid=3477810&group_id=14481&atid=114481 * @param zip file * @throws IOException */ private static void replaceWrongZipByte(File zip) throws IOException { RandomAccessFile r = new RandomAccessFile(zip, "rw"); int flag = Integer.parseInt("00001000", 2); //wrong byte r.seek(7); int realFlags = r.read(); if( (realFlags & flag) > 0) { // in latest versions this bug is fixed, so we're checking is bug exists. r.seek(7); flag = (~flag & 0xff); // removing only wrong bit, other bits remains the same. r.write(realFlags & flag); } r.close(); } 

Update Version:

The following code removes all invalid bytes in a ZIP. KMPMatch.java is easy to find on google

 public static void replaceWrongBytesInZip(File zip) throws IOException { byte find[] = new byte[] { 0, 0x08, 0x08, 0x08, 0 }; int index; while( (index = indexOfBytesInFile(zip,find)) != -1) { replaceWrongZipByte(zip, index + 2); } } private static int indexOfBytesInFile(File file,byte find[]) throws IOException { byte fileContent[] = new byte[(int) file.length()]; FileInputStream fin = new FileInputStream(file); fin.read(fileContent); fin.close(); return KMPMatch.indexOf(fileContent, find); } /** * Replace wrong byte http://sourceforge.net/tracker/?func=detail&aid=3477810&group_id=14481&atid=114481 * @param zip file * @throws IOException */ private static void replaceWrongZipByte(File zip, int wrongByteIndex) throws IOException { RandomAccessFile r = new RandomAccessFile(zip, "rw"); int flag = Integer.parseInt("00001000", 2); r.seek(wrongByteIndex); int realFlags = r.read(); if( (realFlags & flag) > 0) { // in latest versions this bug is fixed, so we're checking is bug exists. r.seek(wrongByteIndex); flag = (~flag & 0xff); // removing only wrong bit, other bits remains the same. r.write(realFlags & flag); } r.close(); } 
+1
source

All Articles