Compress and Decompress Java JAR File with Apache Compress
In this tutorial we demonstrate how to compress and decompress Java JAR files using Apache Compress. In general, JAR
archives are ZIP
files, so the JAR
package supports all options provided by the ZIP
package.
Project Structure
Let’s start by looking at the project structure.
Maven Dependencies
We use Apache Maven to manage our project dependencies. Make sure the following dependencies reside on the class-path. We use Apache Commons Compress, make sure the org.apache.commons:commons-compress
dependency resides on the class-path.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.memorynotfound.io.compression</groupId>
<artifactId>jar</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>IO Compression - ${project.artifactId}</name>
<url>https://memorynotfound.com</url>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.14</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
Compress and Decompress Java JAR File with Apache Compress
JAR
archives are ZIP
files, so the JAR
package supports all options provided by the ZIP
package. To be interoperable JAR
archives should always be created using the UTF-8
encoding for file names (which is the default).
- Compressing files in
JAR
format: We use theJarArchiveOutputStream
to compress files and/or directories intoJAR
format. We can add entries in the archive using theJarArchiveOutputStream.putArchiveEntry
method and pass in aJarArchiveEntry
as an argument containing the file and filename respectively.. - Decompressing
JAR
archive: We can decompress theJAR
archive using theJarArchiveInputStream
class. Next, we loop over theJarArchiveEntry
using theJarArchiveEntry.getNextJarEntry()
class and copy the content to anFileOutputStream
.
Archives created using JarArchiveOutputStream
will implicitly add a JarMaker
extra field to the very first archive entry of the archive which will make Solaris
recognize them as Java archives and allows them to be used as executables.
package com.memorynotfound.resource;
import org.apache.commons.compress.archivers.jar.JarArchiveEntry;
import org.apache.commons.compress.archivers.jar.JarArchiveInputStream;
import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream;
import org.apache.commons.compress.utils.IOUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class JAR {
private JAR() {}
public static void compress(String name, File... files) throws IOException {
try (JarArchiveOutputStream out = new JarArchiveOutputStream(new FileOutputStream(name))){
for (File file : files){
addToArchiveCompression(out, file, ".");
}
}
}
public static void decompress(String in, File destination) throws IOException {
try (JarArchiveInputStream jin = new JarArchiveInputStream(new FileInputStream(in))){
JarArchiveEntry entry;
while ((entry = jin.getNextJarEntry()) != null) {
if (entry.isDirectory()){
continue;
}
File curfile = new File(destination, entry.getName());
File parent = curfile.getParentFile();
if (!parent.exists()) {
if (!parent.mkdirs()){
throw new RuntimeException("could not create directory: " + parent.getPath());
}
}
IOUtils.copy(jin, new FileOutputStream(curfile));
}
}
}
private static void addToArchiveCompression(JarArchiveOutputStream out, File file, String dir) throws IOException {
String name = dir + File.separator + file.getName();
if (file.isFile()){
JarArchiveEntry entry = new JarArchiveEntry(name);
out.putArchiveEntry(entry);
entry.setSize(file.length());
IOUtils.copy(new FileInputStream(file), out);
out.closeArchiveEntry();
} else if (file.isDirectory()) {
File[] children = file.listFiles();
if (children != null){
for (File child : children){
addToArchiveCompression(out, child, name);
}
}
} else {
System.out.println(file.getName() + " is not supported");
}
}
}
Java JAR Example
This program demonstrates the JAR
archive compression decompression example.
package com.memorynotfound.resource;
import java.io.File;
import java.io.IOException;
public class JARProgram {
private static final String OUTPUT_DIRECTORY = "/tmp";
private static final String TAR_GZIP_SUFFIX = ".jar";
private static final String MULTIPLE_RESOURCES = "/example-multiple-resources";
private static final String RECURSIVE_DIRECTORY = "/example-recursive-directory";
private static final String MULTIPLE_RESOURCES_PATH = OUTPUT_DIRECTORY + MULTIPLE_RESOURCES + TAR_GZIP_SUFFIX;
private static final String RECURSIVE_DIRECTORY_PATH = OUTPUT_DIRECTORY + RECURSIVE_DIRECTORY + TAR_GZIP_SUFFIX;
public static void main(String... args) throws IOException {
// class for resource classloading
Class clazz = JARProgram.class;
// get multiple resources files to compress
File resource1 = new File(clazz.getResource("/in/resource1.txt").getFile());
File resource2 = new File(clazz.getResource("/in/resource2.txt").getFile());
File resource3 = new File(clazz.getResource("/in/resource3.txt").getFile());
// compress multiple resources
JAR.compress(MULTIPLE_RESOURCES_PATH, resource1, resource2, resource3);
// decompress multiple resources
JAR.decompress(MULTIPLE_RESOURCES_PATH, new File(OUTPUT_DIRECTORY + MULTIPLE_RESOURCES));
// get directory file to compress
File directory = new File(clazz.getResource("/in/dir").getFile());
// compress recursive directory
JAR.compress(RECURSIVE_DIRECTORY_PATH, directory);
// decompress recursive directory
JAR.decompress(RECURSIVE_DIRECTORY_PATH, new File(OUTPUT_DIRECTORY + RECURSIVE_DIRECTORY));
}
}
Generated Files
Here is an example of the generated JAR
archive.
References
- Apache Commons Compress Official Website
- Apache Commons Compress API JavaDoc
- JarArchiveOutputStream JavaDoc
- JarArchiveInputStream JavaDoc
- JarArchiveEntry JavaDoc
- JarMarker JavaDoc
I tried it and found that there is extra ‘./’ in the root directory after compression, so I added ‘file.getParentFile().getName().equals(“rootDirName”)’ in method addToArchiveCompression