Prab Prab - 4 months ago 12
Java Question

Modifying a text file in a ZIP archive in Java

My use case requires me to open a txt file, say abc.txt which is inside a zip archive which contains key-value pairs in the form


key1=value1

key2=value2


.. and so on where each key-value pair is in a new line.
I have to change one value corresponding to a certain key and put the text file back in a new copy of the archive. How do I do this in java?

My attempt so far:

ZipFile zipFile = new ZipFile("test.zip");
final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("out.zip"));
for(Enumeration e = zipFile.entries(); e.hasMoreElements(); ) {
ZipEntry entryIn = (ZipEntry) e.nextElement();
if(!entryIn.getName().equalsIgnoreCase("abc.txt")){
zos.putNextEntry(entryIn);
InputStream is = zipFile.getInputStream(entryIn);
byte [] buf = new byte[1024];
int len;
while((len = (is.read(buf))) > 0) {
zos.write(buf, 0, len);
}
}
else{
// I'm not sure what to do here
// Tried a few things and the file gets corrupt
}
zos.closeEntry();
}
zos.close();

Answer

You had almost got it right. One possible reason, the file was shown as corrupted is that you might have used

zos.putNextEntry(entryIn)

in the else part as well. This creates a new entry in the zip file containing information from the existing zip file. Existing information contains entry name(file name) and its CRC among other things.

And then, when u try to update the text file and close the zip file, it will throw an error as the CRC defined in the entry and the CRC of the object you are trying to write differ.

Also u might get an error if the length of the text that you are trying to replace is different than the one existing i.e. you are trying to replace

key1=value1

with

key1=val1

This boils down to the problem that the buffer you are trying to write to has length different than the one specified.

ZipFile zipFile = new ZipFile("test.zip");
final ZipOutputStream zos = new ZipOutputStream(new FileOutputStream("out.zip"));
for(Enumeration e = zipFile.entries(); e.hasMoreElements(); ) {
    ZipEntry entryIn = (ZipEntry) e.nextElement();
    if (!entryIn.getName().equalsIgnoreCase("abc.txt")) {
        zos.putNextEntry(entryIn);
        InputStream is = zipFile.getInputStream(entryIn);
        byte[] buf = new byte[1024];
        int len;
        while((len = is.read(buf)) > 0) {            
            zos.write(buf, 0, len);
        }
    }
    else{
        zos.putNextEntry(new ZipEntry("abc.txt"));

        InputStream is = zipFile.getInputStream(entryIn);
        byte[] buf = new byte[1024];
        int len;
        while ((len = (is.read(buf))) > 0) {
            String s = new String(buf);
            if (s.contains("key1=value1")) {
                buf = s.replaceAll("key1=value1", "key1=val2").getBytes();
            }
            zos.write(buf, 0, (len < buf.length) ? len : buf.length);
        }
    }
    zos.closeEntry();
}
zos.close();

The following code ensures that even if data that is replaced is of less length than the original length, no IndexOutOfBoundsExceptions occur.

(len < buf.length) ? len : buf.length

Comments