Keeblebrox
Keeblebrox

Reputation: 382

What is the idiomatic way to copy a ZipEntry into a new ZipFile?

I'm writing a tool to do some minor text replacement in a DOCX file, which is a zipped format. My method is to copy ZipEntry contents from entries in the original file into the modified file using a ZipOutputStream. For most DOCX files this works well, but occasionally I will encounter ZipExceptions regarding discrepancies between the contents I've written and the meta-information contained in the ZipEntry (usually a difference in compressed size).

Here's the code I'm using to copy over contents. I've stripped out error handling and document processing for brevity; I haven't had issues with the document entry so far.

ZipFile         original = new ZipFile(INPUT_FILENAME);
ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(OUTPUT_FILE));
Enumeration     entries = original.entries();
byte[]          buffer = new byte[512];
while (entries.hasMoreElements()) {
    ZipEntry    entry = (ZipEntry)entries.nextElement();
    if  ("word/document.xml".equalsIgnoreCase(entry.getName())) {
        //perform special processing
    }
    else{
        outputStream.putNextEntry(entry);
        InputStream in = original.getInputStream(entry);
        while (0 < in.available()){
            int read = in.read(buffer);
            outputStream.write(buffer,0,read);
        }
        in.close();
    }
    outputStream.closeEntry();
}
outputStream.close();

What is the proper or idiomatic way to directly copy ZipEntry objects from one ZipFile to another?

Upvotes: 3

Views: 7340

Answers (2)

Keeblebrox
Keeblebrox

Reputation: 382

I have found a workaround that avoids the error. By creating a new ZipEntry with only the name field set I'm able to copy over contents without issue.

ZipFile         original = new ZipFile(INPUT_FILENAME);
ZipOutputStream outputStream = new ZipOutputStream(new FileOutputStream(OUTPUT_FILE));
Enumeration     entries = original.entries();
byte[]          buffer = new byte[512];
while (entries.hasMoreElements()) {
    ZipEntry    entry = (ZipEntry)entries.nextElement();
    if  ("word/document.xml".equalsIgnoreCase(entry.getName())) {
        //perform special processing
    }
    else{
        // create a new empty ZipEntry
        ZipEntry newEntry = new ZipEntry(entry.getName()); 
//      outputStream.putNextEntry(entry);
        outputStream.putNextEntry(newEntry);
        InputStream in = original.getInputStream(entry);
        while (0 < in.available()){
            int read = in.read(buffer);
            if (read > 0) {
              outputStream.write(buffer,0,read);
            }
        }
        in.close();
    }
    outputStream.closeEntry();
}
outputStream.close();

However, I this method loses any meta-information stored in the fields of original ZipEntry (e.g.: comment, extra). The API docs aren't clear on whether this is important.

Upvotes: 9

Paul Jowett
Paul Jowett

Reputation: 6581

To keep your meta-data for the zip entry, create it using ZipEntry's "copy constructor":

ZipEntry newEntry = new ZipEntry(entry); 

You can then modify just the name or the comments etc. and everything else will be copied from the given entry.

You could also look at Docmosis which can populate DocX files from Java.

Upvotes: 0

Related Questions