0

I have a problem generating ZIP files via zip4j.

I am able to produce ZIP archive using following code (I omitted some parts, that are not related to the issue), which is basically taken from zip4j tutorial:

File zipFile = new File(zipName);
ZipParameters params = new ZipParameters();
params.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
params.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
byte[] buffer = new byte[8192];
try (ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile))) {
   for (/* loop through list of input data */) {
        String outputName = /* method to determine file name */;
        try (InputStream in = /* method to get IS */ ) {
            params.setFileNameInZip(outputName);
            File tmpEntry = new File(outputName);
            tmpEntry.createNewFile();
            out.putNextEntry(tmpEntry, params);
            int len;
            while ((len = in.read(buffer)) > 0) {
                out.write(buffer, 0, len);
            }
            tmpEntry.delete();
            out.closeEntry();
            in.close();
        }
    }
}

The problem is that although all files are correctly included in ZIP archive, their declared file size is 0. I can unzip them using "dumb" ZIP readers (like build-in TotalCommander's one), because all data are actually here, but more "clever" programs (like 7zip) produce CRC error and refuse to open them as corrupted.

I would say I need to declare the file size somehow (and I am definitely not doing this in my code snippet), but I was unable to find the (probably obvious) solution. I have googled that native java.util.zip.ZipEntry has .setSize() method, but I don't see anything like this in zip4j...

Anyone knows the correct approach to this?

Ellrohir
  • 1,017
  • 1
  • 14
  • 32

1 Answers1

1

So the solution is quite simple. One just needs to add following setting into ZipParameters:

params.setSourceExternalStream(true);

How I found that?

I digged deeper into the code and find following in closeEntry() method of CipherOutputStream.java class of zip4j:

if (zipParameters.isSourceExternalStream()) {
    fileHeader.setUncompressedSize(totalBytesRead);
    if (localFileHeader.getUncompressedSize() != totalBytesRead) {
        localFileHeader.setUncompressedSize(totalBytesRead);
    }
}

This is the only place where setUncompressedSize() - which sounds like having something to do with declared file size - method is called. So I got suspicious and tried to set the parameter in my code. And it does the job.

The lesson learned is that this example of using zip4j is incorrect because it is missing that vital line of code.

Ellrohir
  • 1,017
  • 1
  • 14
  • 32