29

I am using the Zip utility package of Java and wanted to know how to create a zip file with no compression at all. Setting the level to 0 doesn't help. Is this right?

Also, when I used the STORED method, it throws following exception:

java.util.zip.ZipException: STORED entry missing size, compressed size, or crc-32

I can set the size but now following exception is thrown:

java.util.zip.ZipException: invalid entry crc-32 

I am just following all the examples available by searching on the web and I am not able to really understand it properly I guess. It would be great if someone can help me on this and provide me suggestion to correct the problem I might be doing.

Nathan
  • 8,093
  • 8
  • 50
  • 76
  • Those int constants are "a code accent" from another age (probably C)... I wish David Connelly had read about Java enums before he wrote ZipOutputStream. – Christophe Roussy Feb 05 '15 at 10:09
  • 2
    @ChristopheRoussy That would have been rather difficult, as `ZipOutputStream` appeared many years before enums. – user207421 Mar 26 '16 at 00:24
  • 1
    I wonder what is so cool about enums. and what is so uncool about const integers. –  Jan 16 '20 at 21:15

4 Answers4

21

I'm leery of aperkins solution (since deleted), but I know why it worked. The line (which has since been corrected in his answer)

zipOut.setLevel(ZipOutputStream.STORED); // accidentally right

was using the static value ZipOutputStream.STORED, which coincidentally equals 0. So what that line is doing is setting the level used by the default DEFLATED method to zero compression (this is obviously what you want to do, but happened to only work by luck). So to get what you want explicitly and safely, use this instead:

zipOut.setMethod(ZipOutputStream.DEFLATED); // this line optional
zipOut.setLevel(0);

or

zipOut.setLevel(Deflater.NO_COMPRESSION);

If you use

zipOut.setMethod(ZipOutputStream.STORED);
zipOut.setLevel(Deflater.NO_COMPRESSION);

you'll probably get the Exception that Keya noted in the original question. I believe Christian Schlichtherle is right; you are getting the Exceptions because you are not setting the CRC in the entry. The repercussions of that is that to use the STORED method, you have to read the entire entry file first, or find some other way to set the size, compressed size (must be equal) and the CRC before you call zipOut.putNextEntry(). Otherwise, you'll run into more exceptions if you overrun the size attribute by writing too many bytes to the output stream. It appears that the ZIP specs say that if you are writing STORED data then it has to write the header [which includes the CRC-32 and length] "up front" before the data itself, hence the java API requiring these be set before it can start, since it basically only supports streaming out to the final zip file.

rogerdpack
  • 62,887
  • 36
  • 269
  • 388
PMorganCA
  • 730
  • 6
  • 24
  • 3
    DEFLATED + Level(0) resulted in my case to... EACH COMPRESSED FILE (A BIT) BIGGER THAN THE SOURCE FILE :O I'm pretty sure DEFLATED-0 is different from STORE :/ – Andrea Ligios May 22 '13 at 08:59
  • 3
    DEFLATED-0 and STORE _might_ be different, but they have the same objective - a zip file containing uncompressed data. I don't expect that either method will output a file that is equal in size to the source file. However in the question, Keya was having trouble using the STORE method, because of the pre-processing required to get the required arguments. If you want a compressed file that's smaller than the source file, use a different value in setLevel() - but that's not what the question was asking for. – PMorganCA Nov 05 '14 at 15:46
  • Your code failed for me. The zip file would never get completed. So much for not being compressed. –  Jan 16 '20 at 21:28
9

You need to use the STORED method, but this requires that you set the size, compressedSize and crc32 properties of the corresponding ZipEntry before you can call putNextEntry on the ZipOutputStream. You can precompute the CRC-32 by using a Crc32OutputStream.

Christian Schlichtherle
  • 3,125
  • 1
  • 23
  • 47
7

FYI:

In JDK Source of the method [java.util.zip.ZipOutputStream.setLevel(int)]:

public void setLevel(int level) {
    def.setLevel(level);
}

It simply redirect the compression level setting to the field variable [def], which is an instance of [java.util.zip.Deflater].

And in the source code of the class [java.util.zip.Deflater]:

/**
 * Compression level for no compression.
 */
public static final int NO_COMPRESSION = 0;

/**
 * Compression level for fastest compression.
 */
public static final int BEST_SPEED = 1;

/**
 * Compression level for best compression.
 */
public static final int BEST_COMPRESSION = 9;

/**
 * Default compression level.
 */
public static final int DEFAULT_COMPRESSION = -1;

So, I think it will be more readable if you use the constant value [Deflater.NO_COMPRESSION]:

zipOut.setMethod(ZipOutputStream.DEFLATED);
zipOut.setLevel(Deflater.NO_COMPRESSION);
Li Ying
  • 2,261
  • 27
  • 17
0
    public static void zipForStored(String srcFile, String zipFile) throws Exception {
        File file = new File(zipFile);
        if (!file.exists()) file.createNewFile();
        ZipOutputStream outZip = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
        outZip.setMethod(ZipOutputStream.STORED);
        ZipFiles(srcFile, null, "", outZip);
        outZip.finish();
        outZip.close();
    }

    private static void ZipFiles(String folder, FilenameFilter filter, String file, ZipOutputStream zipOutputSteam) throws Exception {
        if (zipOutputSteam == null)
            return;
        File f = new File(folder, file);
        if (f.isFile()) {
            ZipEntry zipEntry = new ZipEntry(file);
            FileInputStream inputStream = new FileInputStream(f);
            zipEntry.setSize(f.length());
            zipEntry.setCompressedSize(f.length());
            zipEntry.setCrc(computeCrc(f));
            zipOutputSteam.putNextEntry(zipEntry);
            int len;
            byte[] buffer = new byte[4096];
            while ((len = inputStream.read(buffer)) != -1) {
                zipOutputSteam.write(buffer, 0, len);
            }
            zipOutputSteam.closeEntry();
            inputStream.close();
        } else {
            String fileList[] = f.list(filter);
            if (fileList.length <= 0) {
                ZipEntry zipEntry = new ZipEntry(file + File.separator);
                zipEntry.setSize(0);
                zipEntry.setCompressedSize(0);
                zipEntry.setCrc(0);
                zipOutputSteam.putNextEntry(zipEntry);
                zipOutputSteam.closeEntry();
            }
            for (int i = 0; i < fileList.length; i++) {
                if (TextUtils.isEmpty(file)) {
                    ZipFiles(folder, filter, fileList[i], zipOutputSteam);
                } else {
                    ZipFiles(folder, filter, file + File.separator + fileList[i], zipOutputSteam);
                }
            }
        }
    }

    /**
     * Computes the CRC checksum for the given file.
     *
     * @param file The file to compute checksum for.
     * @return A CRC32 checksum.
     * @throws IOException If an I/O error occurs.
     */
    private static long computeCrc(File file) throws IOException {
        CRC32 crc = new CRC32();
        InputStream in = new FileInputStream(file);

        try {
            byte[] buf = new byte[8192];
            int n = in.read(buf);
            while (n != -1) {
                crc.update(buf, 0, n);
                n = in.read(buf);
            }
        } finally {
            in.close();
        }
        return crc.getValue();
    }
clzd0792
  • 56
  • 5