How do I extract a tar file in Java? - java

How do I extract a tar (or tar.gz, or tar.bz2) file in Java?

You can do this with the Apache Commons Compress library. You can download the 1.2 version from http://mvnrepository.com/artifact/org.apache.commons/commons-compress/1.2.
Here are two methods: one that unzips a file and another one that untars it. So, for a file
<fileName>tar.gz, you need to first unzip it and after that untar it. Please note that the tar archive may contain folders as well, case in which they need to be created on the local filesystem.
Enjoy.
/** Untar an input file into an output file.
* The output file is created in the output folder, having the same name
* as the input file, minus the '.tar' extension.
*
* #param inputFile the input .tar file
* #param outputDir the output directory file.
* #throws IOException
* #throws FileNotFoundException
*
* #return The {#link List} of {#link File}s with the untared content.
* #throws ArchiveException
*/
private static List<File> unTar(final File inputFile, final File outputDir) throws FileNotFoundException, IOException, ArchiveException {
LOG.info(String.format("Untaring %s to dir %s.", inputFile.getAbsolutePath(), outputDir.getAbsolutePath()));
final List<File> untaredFiles = new LinkedList<File>();
final InputStream is = new FileInputStream(inputFile);
final TarArchiveInputStream debInputStream = (TarArchiveInputStream) new ArchiveStreamFactory().createArchiveInputStream("tar", is);
TarArchiveEntry entry = null;
while ((entry = (TarArchiveEntry)debInputStream.getNextEntry()) != null) {
final File outputFile = new File(outputDir, entry.getName());
if (entry.isDirectory()) {
LOG.info(String.format("Attempting to write output directory %s.", outputFile.getAbsolutePath()));
if (!outputFile.exists()) {
LOG.info(String.format("Attempting to create output directory %s.", outputFile.getAbsolutePath()));
if (!outputFile.mkdirs()) {
throw new IllegalStateException(String.format("Couldn't create directory %s.", outputFile.getAbsolutePath()));
}
}
} else {
LOG.info(String.format("Creating output file %s.", outputFile.getAbsolutePath()));
final OutputStream outputFileStream = new FileOutputStream(outputFile);
IOUtils.copy(debInputStream, outputFileStream);
outputFileStream.close();
}
untaredFiles.add(outputFile);
}
debInputStream.close();
return untaredFiles;
}
/**
* Ungzip an input file into an output file.
* <p>
* The output file is created in the output folder, having the same name
* as the input file, minus the '.gz' extension.
*
* #param inputFile the input .gz file
* #param outputDir the output directory file.
* #throws IOException
* #throws FileNotFoundException
*
* #return The {#File} with the ungzipped content.
*/
private static File unGzip(final File inputFile, final File outputDir) throws FileNotFoundException, IOException {
LOG.info(String.format("Ungzipping %s to dir %s.", inputFile.getAbsolutePath(), outputDir.getAbsolutePath()));
final File outputFile = new File(outputDir, inputFile.getName().substring(0, inputFile.getName().length() - 3));
final GZIPInputStream in = new GZIPInputStream(new FileInputStream(inputFile));
final FileOutputStream out = new FileOutputStream(outputFile);
IOUtils.copy(in, out);
in.close();
out.close();
return outputFile;
}

Note: This functionality was later published through a separate project, Apache Commons Compress, as described in another answer. This answer is out of date.
I haven't used a tar API directly, but tar and bzip2 are implemented in Ant; you could borrow their implementation, or possibly use Ant to do what you need.
Gzip is part of Java SE (and I'm guessing the Ant implementation follows the same model).
GZIPInputStream is just an InputStream decorator. You can wrap, for example, a FileInputStream in a GZIPInputStream and use it in the same way you'd use any InputStream:
InputStream is = new GZIPInputStream(new FileInputStream(file));
(Note that the GZIPInputStream has its own, internal buffer, so wrapping the FileInputStream in a BufferedInputStream would probably decrease performance.)

Archiver archiver = ArchiverFactory.createArchiver("tar", "gz");
archiver.extract(archiveFile, destDir);
Dependency:
<dependency>
<groupId>org.rauschig</groupId>
<artifactId>jarchivelib</artifactId>
<version>0.5.0</version>
</dependency>

Apache Commons VFS supports tar as a virtual file system, which supports URLs like this one tar:gz:http://anyhost/dir/mytar.tar.gz!/mytar.tar!/path/in/tar/README.txt
TrueZip or its successor TrueVFS does the same ... it's also available from Maven Central.

I just tried a bunch of the suggested libs (TrueZip, Apache Compress), but no luck.
Here is an example with Apache Commons VFS:
FileSystemManager fsManager = VFS.getManager();
FileObject archive = fsManager.resolveFile("tgz:file://" + fileName);
// List the children of the archive file
FileObject[] children = archive.getChildren();
System.out.println("Children of " + archive.getName().getURI()+" are ");
for (int i = 0; i < children.length; i++) {
FileObject fo = children[i];
System.out.println(fo.getName().getBaseName());
if (fo.isReadable() && fo.getType() == FileType.FILE
&& fo.getName().getExtension().equals("nxml")) {
FileContent fc = fo.getContent();
InputStream is = fc.getInputStream();
}
}
And the maven dependency:
<dependency>
<groupId>commons-vfs</groupId>
<artifactId>commons-vfs</artifactId>
<version>1.0</version>
</dependency>

In addition to gzip and bzip2, Apache Commons Compress API has also tar support, originally based on ICE Engineering Java Tar Package, which is both API and standalone tool.

What about using this API for tar files, this other one included inside Ant for BZIP2 and the standard one for GZIP?

Here's a version based on this earlier answer by Dan Borza that uses Apache Commons Compress and Java NIO (i.e. Path instead of File). It also does the uncompression and untarring in one stream so there's no intermediate file creation.
public static void unTarGz( Path pathInput, Path pathOutput ) throws IOException {
TarArchiveInputStream tararchiveinputstream =
new TarArchiveInputStream(
new GzipCompressorInputStream(
new BufferedInputStream( Files.newInputStream( pathInput ) ) ) );
ArchiveEntry archiveentry = null;
while( (archiveentry = tararchiveinputstream.getNextEntry()) != null ) {
Path pathEntryOutput = pathOutput.resolve( archiveentry.getName() );
if( archiveentry.isDirectory() ) {
if( !Files.exists( pathEntryOutput ) )
Files.createDirectory( pathEntryOutput );
}
else
Files.copy( tararchiveinputstream, pathEntryOutput );
}
tararchiveinputstream.close();
}

Related

How to read an excel file inside the jar? [duplicate]

I'm trying to access an XML file within a jar file, from a separate jar that's running as a desktop application. I can get the URL to the file I need, but when I pass that to a FileReader (as a String) I get a FileNotFoundException saying "The file name, directory name, or volume label syntax is incorrect."
As a point of reference, I have no trouble reading image resources from the same jar, passing the URL to an ImageIcon constructor. This seems to indicate that the method I'm using to get the URL is correct.
URL url = getClass().getResource("/xxx/xxx/xxx/services.xml");
ServicesLoader jsl = new ServicesLoader( url.toString() );
Inside the ServicesLoader class I have
XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler( this );
xr.setErrorHandler( this );
xr.parse( new InputSource( new FileReader( filename )));
What's wrong with using this technique to read the XML file?
Looks like you want to use java.lang.Class.getResourceAsStream(String), see
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getResourceAsStream-java.lang.String-
You don't say if this is a desktop or web app. I would use the getResourceAsStream() method from an appropriate ClassLoader if it's a desktop or the Context if it's a web app.
It looks as if you are using the URL.toString result as the argument to the FileReader constructor. URL.toString is a bit broken, and instead you should generally use url.toURI().toString(). In any case, the string is not a file path.
Instead, you should either:
Pass the URL to ServicesLoader and let it call openStream or similar.
Use Class.getResourceAsStream and just pass the stream over, possibly inside an InputSource. (Remember to check for nulls as the API is a bit messy.)
The problem was that I was going a step too far in calling the parse method of XMLReader. The parse method accepts an InputSource, so there was no reason to even use a FileReader. Changing the last line of the code above to
xr.parse( new InputSource( filename ));
works just fine.
I'd like to point out that one issues is what if the same resources are in multiple jar files.
Let's say you want to read /org/node/foo.txt, but not from one file, but from each and every jar file.
I have run into this same issue several times before.
I was hoping in JDK 7 that someone would write a classpath filesystem, but alas not yet.
Spring has the Resource class which allows you to load classpath resources quite nicely.
I wrote a little prototype to solve this very problem of reading resources form multiple jar files. The prototype does not handle every edge case, but it does handle looking for resources in directories that are in the jar files.
I have used Stack Overflow for quite sometime. This is the second answer that I remember answering a question so forgive me if I go too long (it is my nature).
This is a prototype resource reader. The prototype is devoid of robust error checking.
I have two prototype jar files that I have setup.
<pre>
<dependency>
<groupId>invoke</groupId>
<artifactId>invoke</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>node</groupId>
<artifactId>node</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
The jar files each have a file under /org/node/ called resource.txt.
This is just a prototype of what a handler would look like with classpath://
I also have a resource.foo.txt in my local resources for this project.
It picks them all up and prints them out.
package com.foo;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Prototype resource reader.
* This prototype is devoid of error checking.
*
*
* I have two prototype jar files that I have setup.
* <pre>
* <dependency>
* <groupId>invoke</groupId>
* <artifactId>invoke</artifactId>
* <version>1.0-SNAPSHOT</version>
* </dependency>
*
* <dependency>
* <groupId>node</groupId>
* <artifactId>node</artifactId>
* <version>1.0-SNAPSHOT</version>
* </dependency>
* </pre>
* The jar files each have a file under /org/node/ called resource.txt.
* <br />
* This is just a prototype of what a handler would look like with classpath://
* I also have a resource.foo.txt in my local resources for this project.
* <br />
*/
public class ClasspathReader {
public static void main(String[] args) throws Exception {
/* This project includes two jar files that each have a resource located
in /org/node/ called resource.txt.
*/
/*
Name space is just a device I am using to see if a file in a dir
starts with a name space. Think of namespace like a file extension
but it is the start of the file not the end.
*/
String namespace = "resource";
//someResource is classpath.
String someResource = args.length > 0 ? args[0] :
//"classpath:///org/node/resource.txt"; It works with files
"classpath:///org/node/"; //It also works with directories
URI someResourceURI = URI.create(someResource);
System.out.println("URI of resource = " + someResourceURI);
someResource = someResourceURI.getPath();
System.out.println("PATH of resource =" + someResource);
boolean isDir = !someResource.endsWith(".txt");
/** Classpath resource can never really start with a starting slash.
* Logically they do, but in reality you have to strip it.
* This is a known behavior of classpath resources.
* It works with a slash unless the resource is in a jar file.
* Bottom line, by stripping it, it always works.
*/
if (someResource.startsWith("/")) {
someResource = someResource.substring(1);
}
/* Use the ClassLoader to lookup all resources that have this name.
Look for all resources that match the location we are looking for. */
Enumeration resources = null;
/* Check the context classloader first. Always use this if available. */
try {
resources =
Thread.currentThread().getContextClassLoader().getResources(someResource);
} catch (Exception ex) {
ex.printStackTrace();
}
if (resources == null || !resources.hasMoreElements()) {
resources = ClasspathReader.class.getClassLoader().getResources(someResource);
}
//Now iterate over the URLs of the resources from the classpath
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
/* if the resource is a file, it just means that we can use normal mechanism
to scan the directory.
*/
if (resource.getProtocol().equals("file")) {
//if it is a file then we can handle it the normal way.
handleFile(resource, namespace);
continue;
}
System.out.println("Resource " + resource);
/*
Split up the string that looks like this:
jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/
into
this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar
and this
/org/node/
*/
String[] split = resource.toString().split(":");
String[] split2 = split[2].split("!");
String zipFileName = split2[0];
String sresource = split2[1];
System.out.printf("After split zip file name = %s," +
" \nresource in zip %s \n", zipFileName, sresource);
/* Open up the zip file. */
ZipFile zipFile = new ZipFile(zipFileName);
/* Iterate through the entries. */
Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
/* If it is a directory, then skip it. */
if (entry.isDirectory()) {
continue;
}
String entryName = entry.getName();
System.out.printf("zip entry name %s \n", entryName);
/* If it does not start with our someResource String
then it is not our resource so continue.
*/
if (!entryName.startsWith(someResource)) {
continue;
}
/* the fileName part from the entry name.
* where /foo/bar/foo/bee/bar.txt, bar.txt is the file
*/
String fileName = entryName.substring(entryName.lastIndexOf("/") + 1);
System.out.printf("fileName %s \n", fileName);
/* See if the file starts with our namespace and ends with our extension.
*/
if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) {
/* If you found the file, print out
the contents fo the file to System.out.*/
try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", entryName, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
//use the entry to see if it's the file '1.txt'
//Read from the byte using file.getInputStream(entry)
}
}
}
/**
* The file was on the file system not a zip file,
* this is here for completeness for this example.
* otherwise.
*
* #param resource
* #param namespace
* #throws Exception
*/
private static void handleFile(URL resource, String namespace) throws Exception {
System.out.println("Handle this resource as a file " + resource);
URI uri = resource.toURI();
File file = new File(uri.getPath());
if (file.isDirectory()) {
for (File childFile : file.listFiles()) {
if (childFile.isDirectory()) {
continue;
}
String fileName = childFile.getName();
if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
try (FileReader reader = new FileReader(childFile)) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", childFile, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
} else {
String fileName = file.getName();
if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
try (FileReader reader = new FileReader(file)) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", fileName, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
You can see a fuller example here with the sample output.
Here's a sample code on how to read a file properly inside a jar file (in this case, the current executing jar file)
Just change executable with the path of your jar file if it is not the current running one.
Then change the filePath to the path of the file you want to use inside the jar file. I.E. if your file is in
someJar.jar\img\test.gif
. Set the filePath to "img\test.gif"
File executable = new File(BrowserViewControl.class.getProtectionDomain().getCodeSource().getLocation().toURI());
JarFile jar = new JarFile(executable);
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
byte[] bytes = new byte[fileInputStreamReader.available()];
int sizeOrig = fileInputStreamReader.available();
int size = fileInputStreamReader.available();
int offset = 0;
while (size != 0){
fileInputStreamReader.read(bytes, offset, size);
offset = sizeOrig - fileInputStreamReader.available();
size = fileInputStreamReader.available();
}
Outside of your technique, why not use the standard Java JarFile class to get the references you want? From there most of your problems should go away.
If you use resources extensively, you might consider using
Commons VFS.
Also supports:
* Local Files
* FTP, SFTP
* HTTP and HTTPS
* Temporary Files "normal FS backed)
* Zip, Jar and Tar (uncompressed, tgz or tbz2)
* gzip and bzip2
* resources
* ram - "ramdrive"
* mime
There's also JBoss VFS - but it's not much documented.
I have 2 CSV files that I use to read data. The java program is exported as a runnable jar file. When you export it, you will figure out it doesn't export your resources with it.
I added a folder under project called data in eclipse. In that folder i stored my csv files.
When I need to reference those files I do it like this...
private static final String ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv";
private static final String ZIP_FILE_LOCATION = "free-zipcode-database.csv";
private static String getFileLocation(){
String loc = new File("").getAbsolutePath() + File.separatorChar +
"data" + File.separatorChar;
if (usePrimaryZipCodesOnly()){
loc = loc.concat(ZIP_FILE_LOCATION_PRIMARY);
} else {
loc = loc.concat(ZIP_FILE_LOCATION);
}
return loc;
}
Then when you put the jar in a location so it can be ran via commandline, make sure that you add the data folder with the resources into the same location as the jar file.

Include file exe into Project Netbeans Java

i want to include a file into my project in Netbeans, i'm developping an application for PC with the language Java. I searched almost on the Net, but i have found nothing. When i compile the application if i go into path where there is /dist the file exe aren't here.
Thank you so much.
String exec [] = {getClass().getClassLoader().getResource("inc_volume.exe").getPath() };
System.out.println(exec[0]);
Runtime.getRuntime().exec(exec);
Update on 20/08/2014 15.29
I have found this source to extract from jar, but i don't know how to use:
java.util.jar.JarFile jar = new java.util.jar.JarFile(jarFile);
java.util.Enumeration enumEntries = jar.entries();
while (enumEntries.hasMoreElements()) {
java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement();
java.io.File f = new java.io.File(destDir + java.io.File.separator + file.getName());
if (file.isDirectory()) { // if its a directory, create it
f.mkdir();
continue;
}
java.io.InputStream is = jar.getInputStream(file); // get the input stream
java.io.FileOutputStream fos = new java.io.FileOutputStream(f);
while (is.available() > 0) { // write contents of 'is' to 'fos'
fos.write(is.read());
}
fos.close();
is.close();
}
Here Image:
To include an exe file to your project, copy this exe file via filesystem to the src folder of your Netbeans project.
when you have built your project, then this exe file will be packaged into the project jar file.
At runtime to run this exe, you will need to extract this exe file from your jar file.
And as this exe file is extracted you can execute it.
To launch an external application from your java code I recommend to use Apache Commons Exec: http://commons.apache.org/proper/commons-exec/
UPDATE
Below there's sample class to demonstrate how to extract all exe files from the current running jar file. I used these SO posts to make this class: the first and the second ones.
import java.io.File;
import java.io.IOException;
/**
*
*/
public class TestClass {
/**
* #param args the command line arguments
*/
public static void main(String[] args) throws IOException {
extractExeFiles("C://Temp");
}
/**
* Gets running jar file path.
* #return running jar file path.
*/
private static File getCurrentJarFilePath() {
return new File(TestClass.class.getProtectionDomain().getCodeSource().getLocation().getPath());
}
/**
* Extracts all exe files to the destination directory.
* #param destDir destination directory.
* #throws IOException if there's an i/o problem.
*/
private static void extractExeFiles(String destDir) throws IOException {
java.util.jar.JarFile jar = new java.util.jar.JarFile(getCurrentJarFilePath());
java.util.Enumeration enumEntries = jar.entries();
String entryName;
while (enumEntries.hasMoreElements()) {
java.util.jar.JarEntry file = (java.util.jar.JarEntry) enumEntries.nextElement();
entryName = file.getName();
if ( (entryName != null) && (entryName.endsWith(".exe"))) {
java.io.File f = new java.io.File(destDir + java.io.File.separator + entryName);
if (file.isDirectory()) { // if its a directory, create it
f.mkdir();
continue;
}
java.io.InputStream is = jar.getInputStream(file); // get the input stream
java.io.FileOutputStream fos = new java.io.FileOutputStream(f);
while (is.available() > 0) { // write contents of 'is' to 'fos'
fos.write(is.read());
}
fos.close();
is.close();
}
}
}
}

How to zip multiple files contained in a folder without zipping the entire folder in android?

This is my requirement I have one folder(say: Main folder) which contains three items
One folder and two text files
I want to zip only these three items contained in the Main folder .Right now I am zipping the contents with the Main folder and the resultant zipped folder name is "temp.zip",when I unzip this,I am getting the "Main folder". But my requirement is when I unzip the "temp.zip",it should display only the contents of the Main folder.
Could any one help me in achieving this?
Thank you.
Edit :This is the code I am using to zip the files
This is the code I am zipping the files
public void zipFolder(String srcFolder, String destZipFile)
throws Exception {
ZipOutputStream zip = null;
FileOutputStream fileWriter = null;
fileWriter = new FileOutputStream(destZipFile);
zip = new ZipOutputStream(fileWriter);
addFolderToZip("", srcFolder, zip);
zip.flush();
zip.close();
}
private void addFolderToZip(String path, String srcFolder,
ZipOutputStream zip) throws Exception {
File folder = new File(srcFolder);
for (String fileName : folder.list()) {
if (path.equals("")) {
addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip);
} else {
addFileToZip(path + "/" + folder.getName(), srcFolder + "/"
+ fileName, zip);
}
}
}
private void addFileToZip(String path, String srcFile, ZipOutputStream zip)
throws Exception {
File folder = new File(srcFile);
if (folder.isDirectory()) {
addFolderToZip(path, srcFile, zip);
} else {
byte[] buf = new byte[1024];
int len;
FileInputStream in = new FileInputStream(srcFile);
zip.putNextEntry(new ZipEntry(path + "/" + folder.getName()));
while ((len = in.read(buf)) > 0) {
zip.write(buf, 0, len);
}
}
}
I am calling the zipfolder method with these params :
zipFolder(srcfolder, destipath + "/" + "temp.zip");
Zipping a set of individual files into a single zip in Android should be pretty straight forward. There's a pretty good tutorial here that should get you started:
http://www.jondev.net/articles/Zipping_Files_with_Android_%28Programmatically%29
I just copied from this post
Referred from the matt answer I successfully used this library.
You can try Zip4j, a pure java library to handle zip file. It supports
encryption/decryption of PKWare and AES encryption methods.
http://www.lingala.net/zip4j/
Key features:
Create, Add, Extract, Update, Remove files from a Zip file
Read/Write password protected Zip files
Supports AES 128/256 Encryption
Supports Standard Zip Encryption
Supports Zip64 format
Supports Store (No Compression) and Deflate compression method
Create or extract files from Split Zip files (Ex: z01, z02,...zip)
Supports Unicode file names
Progress Monitor
License:
Zip4j is released under Apache License, Version 2.0
try {
String zipFile = "/locations/data.zip";
String srcFolder = "/locations";
File folder = new File(srcFolder);
String[] sourceFiles = folder.list();
//create byte buffer
byte[] buffer = new byte[1024];
/*
* To create a zip file, use
*
* ZipOutputStream(OutputStream out) constructor of ZipOutputStream
* class.
*/
//create object of FileOutputStream
FileOutputStream fout = new FileOutputStream(zipFile);
//create object of ZipOutputStream from FileOutputStream
ZipOutputStream zout = new ZipOutputStream(fout);
for (int i = 0; i < sourceFiles.length; i++) {
if (sourceFiles[i].equalsIgnoreCase("file.csv") || sourceFiles[i].equalsIgnoreCase("file1.csv")) {
sourceFiles[i] = srcFolder + fs + sourceFiles[i];
System.out.println("Adding " + sourceFiles[i]);
//create object of FileInputStream for source file
FileInputStream fin = new FileInputStream(sourceFiles[i]);
/*
* To begin writing ZipEntry in the zip file, use
*
* void putNextEntry(ZipEntry entry) method of
* ZipOutputStream class.
*
* This method begins writing a new Zip entry to the zip
* file and positions the stream to the start of the entry
* data.
*/
zout.putNextEntry(new ZipEntry(sourceFiles[i].substring(sourceFiles[i].lastIndexOf("/") + 1)));
/*
* After creating entry in the zip file, actually write the
* file.
*/
int length;
while ((length = fin.read(buffer)) > 0) {
zout.write(buffer, 0, length);
}
/*
* After writing the file to ZipOutputStream, use
*
* void closeEntry() method of ZipOutputStream class to
* close the current entry and position the stream to write
* the next entry.
*/
zout.closeEntry();
//close the InputStream
fin.close();
}
}
//close the ZipOutputStream
zout.close();
System.out.println("Zip file has been created!");
} catch (IOException ioe) {
System.out.println("IOException :" + ioe);
}
In windows you can achieve this by the following steps,
1.Open the main folder and select the files which you want to add into zip file
2.Right click -> Add to archieve
3.Choose the archieve format as zip and click 'Ok'

Using java to extract .rar files

I am looking a ways to unzip .rar files using Java and where ever I search i keep ending up with the same tool - JavaUnRar. I have been looking into unzipping .rar files with this but all the ways i seem to find to do this are very long and awkward like in this example
I am currently able to extract .tar, .tar.gz, .zip and .jar files in 20 lines of code or less so there must be a simpler way to extract .rar files, does anybody know?
Just if it helps anybody this is the code that I am using to extract both .zip and .jar files, it works for both
public void getZipFiles(String zipFile, String destFolder) throws IOException {
BufferedOutputStream dest = null;
ZipInputStream zis = new ZipInputStream(
new BufferedInputStream(
new FileInputStream(zipFile)));
ZipEntry entry;
while (( entry = zis.getNextEntry() ) != null) {
System.out.println( "Extracting: " + entry.getName() );
int count;
byte data[] = new byte[BUFFER];
if (entry.isDirectory()) {
new File( destFolder + "/" + entry.getName() ).mkdirs();
continue;
} else {
int di = entry.getName().lastIndexOf( '/' );
if (di != -1) {
new File( destFolder + "/" + entry.getName()
.substring( 0, di ) ).mkdirs();
}
}
FileOutputStream fos = new FileOutputStream( destFolder + "/"
+ entry.getName() );
dest = new BufferedOutputStream( fos );
while (( count = zis.read( data ) ) != -1)
dest.write( data, 0, count );
dest.flush();
dest.close();
}
}
You are able to extract .gz, .zip, .jar files as they use number of compression algorithms built into the Java SDK.
The case with RAR format is a bit different. RAR is a proprietary archive file format. RAR license does not allow to include it into software development tools like Java SDK.
The best way to unrar your files will be using 3rd party libraries such as junrar.
You can find some references to other Java RAR libraries in SO question RAR archives with java. Also SO question How to compress text file to rar format using java program explains more on different workarounds (e.g. using Runtime).
You can use the library junrar
<dependency>
<groupId>com.github.junrar</groupId>
<artifactId>junrar</artifactId>
<version>0.7</version>
</dependency>
Code example:
File f = new File(filename);
Archive archive = new Archive(f);
archive.getMainHeader().print();
FileHeader fh = archive.nextFileHeader();
while(fh!=null){
File fileEntry = new File(fh.getFileNameString().trim());
System.out.println(fileEntry.getAbsolutePath());
FileOutputStream os = new FileOutputStream(fileEntry);
archive.extractFile(fh, os);
os.close();
fh=archive.nextFileHeader();
}
You can use http://sevenzipjbind.sourceforge.net/index.html
In addition to supporting a large number of archive formats, version 16.02-2.01 has full support for RAR5 extraction with:
password protected archives
archives with encrypted headers
archives splitted in volumes
gradle
implementation 'net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01'
implementation 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01'
or maven
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-all-platforms</artifactId>
<version>16.02-2.01</version>
</dependency>
And code example
import net.sf.sevenzipjbinding.ExtractOperationResult;
import net.sf.sevenzipjbinding.IInArchive;
import net.sf.sevenzipjbinding.SevenZip;
import net.sf.sevenzipjbinding.impl.RandomAccessFileInStream;
import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
/**
* Responsible for unpacking archives with the RAR extension.
* Support Rar4, Rar4 with password, Rar5, Rar5 with password.
* Determines the type of archive itself.
*/
public class RarExtractor {
/**
* Extracts files from archive. Archive can be encrypted with password
*
* #param filePath path to .rar file
* #param password string password for archive
* #return map of extracted file with file name
* #throws IOException
*/
public Map<InputStream, String> extract(String filePath, String password) throws IOException {
Map<InputStream, String> extractedMap = new HashMap<>();
RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");
RandomAccessFileInStream randomAccessFileStream = new RandomAccessFileInStream(randomAccessFile);
IInArchive inArchive = SevenZip.openInArchive(null, randomAccessFileStream);
for (ISimpleInArchiveItem item : inArchive.getSimpleInterface().getArchiveItems()) {
if (!item.isFolder()) {
ExtractOperationResult result = item.extractSlow(data -> {
extractedMap.put(new BufferedInputStream(new ByteArrayInputStream(data)), item.getPath());
return data.length;
}, password);
if (result != ExtractOperationResult.OK) {
throw new RuntimeException(
String.format("Error extracting archive. Extracting error: %s", result));
}
}
}
return extractedMap;
}
}
P.S.
#BorisBrodski https://github.com/borisbrodski Happy 40th birthday to you! Hope you had a great celebration. Thanks for your work!
you could simply add this maven dependency to you code:
<dependency>
<groupId>com.github.junrar</groupId>
<artifactId>junrar</artifactId>
<version>0.7</version>
</dependency>
and then use this code for extract rar file:
File rar = new File("path_to_rar_file.rar");
File tmpDir = File.createTempFile("bip.",".unrar");
if(!(tmpDir.delete())){
throw new IOException("Could not delete temp file: " + tmpDir.getAbsolutePath());
}
if(!(tmpDir.mkdir())){
throw new IOException("Could not create temp directory: " + tmpDir.getAbsolutePath());
}
System.out.println("tmpDir="+tmpDir.getAbsolutePath());
ExtractArchive extractArchive = new ExtractArchive();
extractArchive.extractArchive(rar, tmpDir);
System.out.println("finished.");
This method helps to extract files to streams from rar(RAR5) file stream if you have input stream. In my case I was processing MimeBodyPart from email.
The example from #Alexey Bril didn't work for me.
Dependencies are the same
Gradle
implementation 'net.sf.sevenzipjbinding:sevenzipjbinding:16.02-2.01'
implementation 'net.sf.sevenzipjbinding:sevenzipjbinding-all-platforms:16.02-2.01'
Maven
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding</artifactId>
<version>16.02-2.01</version>
</dependency>
<dependency>
<groupId>net.sf.sevenzipjbinding</groupId>
<artifactId>sevenzipjbinding-all-platforms</artifactId>
<version>16.02-2.01</version>
</dependency>
Code
private List<InputStream> getInputStreamsFromRar5InputStream(InputStream is) throws IOException {
List<InputStream> inputStreams = new ArrayList<>();
File tempFile = File.createTempFile("tempRarArchive-", ".rar", null);
try (FileOutputStream fos = new FileOutputStream(tempFile)) {
fos.write(is.readAllBytes());
fos.flush();
try (RandomAccessFile raf = new RandomAccessFile(tempFile, "r")) {// open for reading
try (IInArchive inArchive = SevenZip.openInArchive(null, // autodetect archive type
new RandomAccessFileInStream(raf))) {
// Getting simple interface of the archive inArchive
ISimpleInArchive simpleInArchive = inArchive.getSimpleInterface();
for (ISimpleInArchiveItem item : simpleInArchive.getArchiveItems()) {
if (!item.isFolder()) {
ExtractOperationResult result;
final InputStream[] IS = new InputStream[1];
final Integer[] sizeArray = new Integer[1];
result = item.extractSlow(new ISequentialOutStream() {
/**
* #param bytes of extracted data
* #return size of extracted data
*/
#Override
public int write(byte[] bytes) {
InputStream is = new ByteArrayInputStream(bytes);
sizeArray[0] = bytes.length;
IS[0] = new BufferedInputStream(is); // Data to write to file
return sizeArray[0];
}
});
if (result == ExtractOperationResult.OK) {
inputStreams.add(IS[0]);
} else {
log.error("Error extracting item: " + result);
}
}
}
}
}
} finally {
tempFile.delete();
}
return inputStreams;
}

How do I read a resource file from a Java jar file?

I'm trying to access an XML file within a jar file, from a separate jar that's running as a desktop application. I can get the URL to the file I need, but when I pass that to a FileReader (as a String) I get a FileNotFoundException saying "The file name, directory name, or volume label syntax is incorrect."
As a point of reference, I have no trouble reading image resources from the same jar, passing the URL to an ImageIcon constructor. This seems to indicate that the method I'm using to get the URL is correct.
URL url = getClass().getResource("/xxx/xxx/xxx/services.xml");
ServicesLoader jsl = new ServicesLoader( url.toString() );
Inside the ServicesLoader class I have
XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler( this );
xr.setErrorHandler( this );
xr.parse( new InputSource( new FileReader( filename )));
What's wrong with using this technique to read the XML file?
Looks like you want to use java.lang.Class.getResourceAsStream(String), see
https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getResourceAsStream-java.lang.String-
You don't say if this is a desktop or web app. I would use the getResourceAsStream() method from an appropriate ClassLoader if it's a desktop or the Context if it's a web app.
It looks as if you are using the URL.toString result as the argument to the FileReader constructor. URL.toString is a bit broken, and instead you should generally use url.toURI().toString(). In any case, the string is not a file path.
Instead, you should either:
Pass the URL to ServicesLoader and let it call openStream or similar.
Use Class.getResourceAsStream and just pass the stream over, possibly inside an InputSource. (Remember to check for nulls as the API is a bit messy.)
The problem was that I was going a step too far in calling the parse method of XMLReader. The parse method accepts an InputSource, so there was no reason to even use a FileReader. Changing the last line of the code above to
xr.parse( new InputSource( filename ));
works just fine.
I'd like to point out that one issues is what if the same resources are in multiple jar files.
Let's say you want to read /org/node/foo.txt, but not from one file, but from each and every jar file.
I have run into this same issue several times before.
I was hoping in JDK 7 that someone would write a classpath filesystem, but alas not yet.
Spring has the Resource class which allows you to load classpath resources quite nicely.
I wrote a little prototype to solve this very problem of reading resources form multiple jar files. The prototype does not handle every edge case, but it does handle looking for resources in directories that are in the jar files.
I have used Stack Overflow for quite sometime. This is the second answer that I remember answering a question so forgive me if I go too long (it is my nature).
This is a prototype resource reader. The prototype is devoid of robust error checking.
I have two prototype jar files that I have setup.
<pre>
<dependency>
<groupId>invoke</groupId>
<artifactId>invoke</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>node</groupId>
<artifactId>node</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
The jar files each have a file under /org/node/ called resource.txt.
This is just a prototype of what a handler would look like with classpath://
I also have a resource.foo.txt in my local resources for this project.
It picks them all up and prints them out.
package com.foo;
import java.io.File;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
import java.net.URL;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Prototype resource reader.
* This prototype is devoid of error checking.
*
*
* I have two prototype jar files that I have setup.
* <pre>
* <dependency>
* <groupId>invoke</groupId>
* <artifactId>invoke</artifactId>
* <version>1.0-SNAPSHOT</version>
* </dependency>
*
* <dependency>
* <groupId>node</groupId>
* <artifactId>node</artifactId>
* <version>1.0-SNAPSHOT</version>
* </dependency>
* </pre>
* The jar files each have a file under /org/node/ called resource.txt.
* <br />
* This is just a prototype of what a handler would look like with classpath://
* I also have a resource.foo.txt in my local resources for this project.
* <br />
*/
public class ClasspathReader {
public static void main(String[] args) throws Exception {
/* This project includes two jar files that each have a resource located
in /org/node/ called resource.txt.
*/
/*
Name space is just a device I am using to see if a file in a dir
starts with a name space. Think of namespace like a file extension
but it is the start of the file not the end.
*/
String namespace = "resource";
//someResource is classpath.
String someResource = args.length > 0 ? args[0] :
//"classpath:///org/node/resource.txt"; It works with files
"classpath:///org/node/"; //It also works with directories
URI someResourceURI = URI.create(someResource);
System.out.println("URI of resource = " + someResourceURI);
someResource = someResourceURI.getPath();
System.out.println("PATH of resource =" + someResource);
boolean isDir = !someResource.endsWith(".txt");
/** Classpath resource can never really start with a starting slash.
* Logically they do, but in reality you have to strip it.
* This is a known behavior of classpath resources.
* It works with a slash unless the resource is in a jar file.
* Bottom line, by stripping it, it always works.
*/
if (someResource.startsWith("/")) {
someResource = someResource.substring(1);
}
/* Use the ClassLoader to lookup all resources that have this name.
Look for all resources that match the location we are looking for. */
Enumeration resources = null;
/* Check the context classloader first. Always use this if available. */
try {
resources =
Thread.currentThread().getContextClassLoader().getResources(someResource);
} catch (Exception ex) {
ex.printStackTrace();
}
if (resources == null || !resources.hasMoreElements()) {
resources = ClasspathReader.class.getClassLoader().getResources(someResource);
}
//Now iterate over the URLs of the resources from the classpath
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
/* if the resource is a file, it just means that we can use normal mechanism
to scan the directory.
*/
if (resource.getProtocol().equals("file")) {
//if it is a file then we can handle it the normal way.
handleFile(resource, namespace);
continue;
}
System.out.println("Resource " + resource);
/*
Split up the string that looks like this:
jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/
into
this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar
and this
/org/node/
*/
String[] split = resource.toString().split(":");
String[] split2 = split[2].split("!");
String zipFileName = split2[0];
String sresource = split2[1];
System.out.printf("After split zip file name = %s," +
" \nresource in zip %s \n", zipFileName, sresource);
/* Open up the zip file. */
ZipFile zipFile = new ZipFile(zipFileName);
/* Iterate through the entries. */
Enumeration entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = entries.nextElement();
/* If it is a directory, then skip it. */
if (entry.isDirectory()) {
continue;
}
String entryName = entry.getName();
System.out.printf("zip entry name %s \n", entryName);
/* If it does not start with our someResource String
then it is not our resource so continue.
*/
if (!entryName.startsWith(someResource)) {
continue;
}
/* the fileName part from the entry name.
* where /foo/bar/foo/bee/bar.txt, bar.txt is the file
*/
String fileName = entryName.substring(entryName.lastIndexOf("/") + 1);
System.out.printf("fileName %s \n", fileName);
/* See if the file starts with our namespace and ends with our extension.
*/
if (fileName.startsWith(namespace) && fileName.endsWith(".txt")) {
/* If you found the file, print out
the contents fo the file to System.out.*/
try (Reader reader = new InputStreamReader(zipFile.getInputStream(entry))) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("zip fileName = %s\n\n####\n contents of file %s\n###\n", entryName, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
//use the entry to see if it's the file '1.txt'
//Read from the byte using file.getInputStream(entry)
}
}
}
/**
* The file was on the file system not a zip file,
* this is here for completeness for this example.
* otherwise.
*
* #param resource
* #param namespace
* #throws Exception
*/
private static void handleFile(URL resource, String namespace) throws Exception {
System.out.println("Handle this resource as a file " + resource);
URI uri = resource.toURI();
File file = new File(uri.getPath());
if (file.isDirectory()) {
for (File childFile : file.listFiles()) {
if (childFile.isDirectory()) {
continue;
}
String fileName = childFile.getName();
if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
try (FileReader reader = new FileReader(childFile)) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", childFile, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
} else {
String fileName = file.getName();
if (fileName.startsWith(namespace) && fileName.endsWith("txt")) {
try (FileReader reader = new FileReader(file)) {
StringBuilder builder = new StringBuilder();
int ch = 0;
while ((ch = reader.read()) != -1) {
builder.append((char) ch);
}
System.out.printf("fileName = %s\n\n####\n contents of file %s\n###\n", fileName, builder);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
}
}
You can see a fuller example here with the sample output.
Here's a sample code on how to read a file properly inside a jar file (in this case, the current executing jar file)
Just change executable with the path of your jar file if it is not the current running one.
Then change the filePath to the path of the file you want to use inside the jar file. I.E. if your file is in
someJar.jar\img\test.gif
. Set the filePath to "img\test.gif"
File executable = new File(BrowserViewControl.class.getProtectionDomain().getCodeSource().getLocation().toURI());
JarFile jar = new JarFile(executable);
InputStream fileInputStreamReader = jar.getInputStream(jar.getJarEntry(filePath));
byte[] bytes = new byte[fileInputStreamReader.available()];
int sizeOrig = fileInputStreamReader.available();
int size = fileInputStreamReader.available();
int offset = 0;
while (size != 0){
fileInputStreamReader.read(bytes, offset, size);
offset = sizeOrig - fileInputStreamReader.available();
size = fileInputStreamReader.available();
}
Outside of your technique, why not use the standard Java JarFile class to get the references you want? From there most of your problems should go away.
If you use resources extensively, you might consider using
Commons VFS.
Also supports:
* Local Files
* FTP, SFTP
* HTTP and HTTPS
* Temporary Files "normal FS backed)
* Zip, Jar and Tar (uncompressed, tgz or tbz2)
* gzip and bzip2
* resources
* ram - "ramdrive"
* mime
There's also JBoss VFS - but it's not much documented.
I have 2 CSV files that I use to read data. The java program is exported as a runnable jar file. When you export it, you will figure out it doesn't export your resources with it.
I added a folder under project called data in eclipse. In that folder i stored my csv files.
When I need to reference those files I do it like this...
private static final String ZIP_FILE_LOCATION_PRIMARY = "free-zipcode-database-Primary.csv";
private static final String ZIP_FILE_LOCATION = "free-zipcode-database.csv";
private static String getFileLocation(){
String loc = new File("").getAbsolutePath() + File.separatorChar +
"data" + File.separatorChar;
if (usePrimaryZipCodesOnly()){
loc = loc.concat(ZIP_FILE_LOCATION_PRIMARY);
} else {
loc = loc.concat(ZIP_FILE_LOCATION);
}
return loc;
}
Then when you put the jar in a location so it can be ran via commandline, make sure that you add the data folder with the resources into the same location as the jar file.

Categories

Resources