Basically, I have a jar file that i want to unzip to a specific folder from a junit test.
What is the easiest way to do this?
I am willing to use a free third party library if it's necessary.
You could use java.util.jar.JarFile to iterate over the entries in the file, extracting each one via its InputStream and writing the data out to an external File. Apache Commons IO provides utilities to make this a bit less clumsy.
ZipInputStream in = null;
OutputStream out = null;
try {
// Open the jar file
String inFilename = "infile.jar";
in = new ZipInputStream(new FileInputStream(inFilename));
// Get the first entry
ZipEntry entry = in.getNextEntry();
// Open the output file
String outFilename = "o";
out = new FileOutputStream(outFilename);
// Transfer bytes from the ZIP file to the output file
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} catch (IOException e) {
// Manage exception
} finally {
// Close the streams
if (out != null) {
out.close();
}
if (in != null) {
in.close();
}
}
Jar is basically zipped using ZIP algorithm, so you can use winzip or winrar to extract.
If you are looking for programmatic way then the first answer is more correct.
From command line type jar xf foo.jar or unzip foo.jar
Use the Ant unzip task.
Here's my version in Scala, would be the same in Java, that unpacks into separate files and directories:
import java.io.{BufferedInputStream, BufferedOutputStream, ByteArrayInputStream}
import java.io.{File, FileInputStream, FileOutputStream}
import java.util.jar._
def unpackJar(jar: File, target: File): Seq[File] = {
val b = Seq.newBuilder[File]
val in = new JarInputStream(new BufferedInputStream(new FileInputStream(jar)))
try while ({
val entry: JarEntry = in.getNextJarEntry
(entry != null) && {
val f = new File(target, entry.getName)
if (entry.isDirectory) {
f.mkdirs()
} else {
val bs = new BufferedOutputStream(new FileOutputStream(f))
try {
val arr = new Array[Byte](1024)
while ({
val sz = in.read(arr, 0, 1024)
(sz > 0) && { bs.write(arr, 0, sz); true }
}) ()
} finally {
bs.close()
}
}
b += f
true
}
}) () finally {
in.close()
}
b.result()
}
Related
I have been working on a task to decompress from different types of file format such as "zip,tar,tbz,tgz". I am able to do for all except tbz because apache common compress library provides BZIP2 compressors. But I need to decompress a old BZIP not BZIP2. Is there any way to do it java. I have added the code I have done so far for extracting different tar file archives using apache commons library below.
public List<ArchiveFile> processTarFiles(String compressedFilePath, String fileType) throws IOException {
List<ArchiveFile> extractedFileList = null;
TarArchiveInputStream is = null;
FileOutputStream fos = null;
BufferedOutputStream dest = null;
try {
if(fileType.equalsIgnoreCase("tar"))
{
is = new TarArchiveInputStream(new FileInputStream(new File(compressedFilePath)));
}
else if(fileType.equalsIgnoreCase("tbz")||fileType.equalsIgnoreCase("bz"))
{
is = new TarArchiveInputStream(new BZip2CompressorInputStream(new FileInputStream(new File(compressedFilePath))));
}
else if(fileType.equalsIgnoreCase("tgz")||fileType.equalsIgnoreCase("gz"))
{
is = new TarArchiveInputStream(new GzipCompressorInputStream(new FileInputStream(new File(compressedFilePath))));
}
TarArchiveEntry entry = is.getNextTarEntry();
extractedFileList = new ArrayList<>();
while (entry != null) {
// grab a zip file entry
String currentEntry = entry.getName();
if (!entry.isDirectory()) {
File destFile = new File(Constants.DEFAULT_ZIPOUTPUTPATH, currentEntry);
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
ArchiveFile archiveFile = new ArchiveFile();
int currentByte;
// establish buffer for writing file
byte data[] = new byte[(int) entry.getSize()];
// write the current file to disk
fos = new FileOutputStream(destFile);
dest = new BufferedOutputStream(fos, (int) entry.getSize());
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, (int) entry.getSize())) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
dest.close();
archiveFile.setExtractedFilePath(destFile.getAbsolutePath());
archiveFile.setFormat(destFile.getName().split("\\.")[1]);
extractedFileList.add(archiveFile);
entry = is.getNextTarEntry();
} else {
new File(Constants.DEFAULT_ZIPOUTPUTPATH, currentEntry).mkdirs();
entry = is.getNextTarEntry();
}
}
} catch (IOException e) {
System.out.println(("ERROR: " + e.getMessage()));
} catch (Exception e) {
System.out.println(("ERROR: " + e.getMessage()));
} finally {
is.close();
dest.flush();
dest.close();
}
return extractedFileList;
}
The original Bzip was supposedly using a patented algorithm so Bzip2 was born using algorithms and techniques that were not patented.
That might be the reason why it's no longer in widespread use and open source libraries ignore it.
There's some C code for decompressing Bzip files shown here (gist.github.com mirror).
You might want to read and rewrite that in Java.
Using Java I have to read text files which are inside gz file which is in another .tar.gz
gz_ltm_logs.tar.gz is the filename. It then has files ltm.1.gz, ltm.2.gz inside it and then these files have text files in them.
I wanted to do it using java.util.zip.* only but if it is impossible then I can look at other libraries.
I thought I will be able to do it using java.util.zip. But doesn't seem straightforward
Here's some code to give you an idea. This method will try to extract a given tar.gz file to outputFolder.
public static void extract(File input, File outputFolder) throws IOException {
byte[] buffer = new byte[1024];
GZIPInputStream gzipFile = new GZIPInputStream(new FileInputStream(input));
ByteOutputStream tarStream = new ByteOutputStream();
int gzipLengthRead;
while ((gzipLengthRead = gzipFile.read(buffer)) > 0){
tarStream.write(buffer, 0, gzipLengthRead);
}
gzipFile.close();
org.apache.tools.tar.TarInputStream tarFile = null;
// files inside the tar
OutputStream out = null;
try {
tarFile = new org.apache.tools.tar.TarInputStream(tarStream.newInputStream());
tarStream.close();
TarEntry entry = null;
while ((entry = tarFile.getNextEntry()) != null) {
String outFilename = entry.getName();
if (entry.isDirectory()) {
File directory = new File(outputFolder, outFilename);
directory.mkdirs();
} else {
File outputFile = new File(outputFolder, outFilename);
File outputDirectory = outputFile.getParentFile();
if (!outputDirectory.exists()) {
outputDirectory.mkdirs();
}
out = new FileOutputStream(outputFile);
// Transfer bytes from the tarFile to the output file
int innerLen;
while ((innerLen = tarFile.read(buffer)) > 0) {
out.write(buffer, 0, innerLen);
}
out.close();
}
}
} finally {
if (tarFile != null) {
tarFile.close();
}
if (out != null) {
out.close();
}
}
}
I have two jar files. Normally if I want to 'unpack' resource from my jar file I go for :
InputStream in = MyClass.class.getClassLoader().getResourceAsStream(name);
byte[] buffer = new byte[1024];
int read = -1;
File temp2 = new File(new File(System.getProperty("user.dir")), name);
FileOutputStream fos2 = new FileOutputStream(temp2);
while((read = in.read(buffer)) != -1) {
fos2.write(buffer, 0, read);
}
fos2.close();
in.close();
What If I would have another JAR files in the same directory? Can I access the second JAR file resources in simillar way? This second JAR is not runned so don't have own class loader. Is the only way to unzip this second JAR file?
I've used the below mentioned code to do the same kind of operation. It uses JarFile class to do the same.
/**
* Copies a directory from a jar file to an external directory.
*/
public static void copyResourcesToDirectory(JarFile fromJar, String jarDir, String destDir)
throws IOException {
for (Enumeration<JarEntry> entries = fromJar.entries(); entries.hasMoreElements();) {
JarEntry entry = entries.nextElement();
if (entry.getName().startsWith(jarDir + "/") && !entry.isDirectory()) {
File dest = new File(destDir + "/" + entry.getName().substring(jarDir.length() + 1));
File parent = dest.getParentFile();
if (parent != null) {
parent.mkdirs();
}
FileOutputStream out = new FileOutputStream(dest);
InputStream in = fromJar.getInputStream(entry);
try {
byte[] buffer = new byte[8 * 1024];
int s = 0;
while ((s = in.read(buffer)) > 0) {
out.write(buffer, 0, s);
}
} catch (IOException e) {
throw new IOException("Could not copy asset from jar file", e);
} finally {
try {
in.close();
} catch (IOException ignored) {}
try {
out.close();
} catch (IOException ignored) {}
}
}
}
If the other Jar is in your regular classpath then you can simply access the resource in that jar in the exact same way. If the Jar is just an file that's not on your classpath you will have to instead open it and extract the file with the JarFile and related classes. Note that Jar files are just special types of Zip files, so you can also access a Jar file with the ZipFile related classes
You can use URLClassLoader.
URLClassLoader classLoader = new URLClassLoader(new URL[]{new URL("path_to_file//myjar.jar")})
classLoader.loadClass("MyClass");//is requared
InputStream stream = classLoader.getResourceAsStream("myresource.properties");
Is this an efficient way to copy all files with in a directory, including child directories? Is there a chance of infinite recursion? Is there anything I should change? I know it works, but I think there should be an easier way to do this.
private void copy(File file, String path) {
String fileName = file.getPath();
System.out.println(fileName);
fileName = fileName.substring(fileName.lastIndexOf("\\"));
if (path == null)
path = Storage.getStorageDirectoryPath();
File toWrite = new File(path + File.separator + fileName);
if (file.isDirectory()) {
toWrite.mkdir();
File inDirectory[] = file.listFiles();
for (File f : inDirectory)
copy(f, toWrite.getPath());
} else {
try {
InputStream inStream = new FileInputStream(file);
OutputStream outStream = new FileOutputStream(toWrite);
byte buffer[] = new byte[1024];
int length = 0;
while ((length = inStream.read(buffer)) > 0) {
outStream.write(buffer, 0, length);
}
inStream.close();
outStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Thanks
Looks pretty good as the comments indicate. You might want to look into the new Java 7 API's (the new NIO). There's a tutorial here, it looks like there are even options to avoid following links.
If you can't use Java 7, old NIO has channels that you can open after opening the file the old way. They include methods transferFrom and transferTo, which might be able to do it more efficiently than you could in Java.
Why reinvent the wheel? take a look at the methods in Apache Common's FileUtils, in particular copyDirectory.
I'm having a hard time trying to tar some files using the compress library.
My code is the following, and is taken from the commons.compress wiki exemples :
private static File createTarFile(String[] filePaths, String saveAs) throws Exception{
File tarFile = new File(saveAs);
OutputStream out = new FileOutputStream(tarFile);
TarArchiveOutputStream aos = (TarArchiveOutputStream) new ArchiveStreamFactory().createArchiveOutputStream("tar", out);
for(String filePath : filePaths){
File file = new File(filePath);
TarArchiveEntry entry = new TarArchiveEntry(file);
entry.setSize(file.length());
aos.putArchiveEntry(entry);
IOUtils.copy(new FileInputStream(file), aos);
aos.closeArchiveEntry();
}
aos.finish();
out.close();
return tarFile;
}
There is no error during the process, but when I try to untar the file, I got the following :
XXXX:XXXX /home/XXXX$ tar -xf typeCommandes.tar
tar: Unexpected EOF in archive
tar: Unexpected EOF in archive
tar: Error is not recoverable: exiting now
Also, the archive IS slighty smaller in size than the original file, which isnt normal for a tar, so there DO is a problem...
-rw-r--r-- 1 XXXX nobody 12902400 Jan 14 17:11 typeCommandes.tar
-rw-r--r-- 1 XXXX nobody 12901888 Jan 14 17:16 typeCommandes.csv
Anyone can tell me what I'm doing wrong ? Thanks
You're not closing the TarArchiveOutputStream. Add aos.close() after aos.finish()
Small correction to the code above.
It does not close input stream, while Apache lib assumes that stream is managed by calling client.
See the fix below (put this code after the line 'aos.putArchiveEntry(entry)') :
FileInputStream fis = new FileInputStream(fileForPuttingIntoTar);
IOUtils.copy(fis, aos);
fis.close();
aos.closeArchiveEntry();
the example here -> http://commons.apache.org/compress/examples.html uses the method putNextEntry(entry) which you seem to omit.
See also my answer here
import org.apache.commons.compress.archivers.ArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveEntry;
import org.apache.commons.compress.archivers.tar.TarArchiveInputStream;
import org.apache.commons.compress.archivers.tar.TarArchiveOutputStream;
public class TarUpdater {
private static final int buffersize = 8048;
public static void updateFile(File tarFile, File[] flist) throws IOException {
// get a temp file
File tempFile = File.createTempFile(tarFile.getName(), null);
// delete it, otherwise you cannot rename your existing tar to it.
if (tempFile.exists()) {
tempFile.delete();
}
if (!tarFile.exists()) {
tarFile.createNewFile();
}
boolean renameOk = tarFile.renameTo(tempFile);
if (!renameOk) {
throw new RuntimeException(
"could not rename the file " + tarFile.getAbsolutePath() + " to " + tempFile.getAbsolutePath());
}
byte[] buf = new byte[buffersize];
TarArchiveInputStream tin = new TarArchiveInputStream(new FileInputStream(tempFile));
OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(tarFile.toPath()));
TarArchiveOutputStream tos = new TarArchiveOutputStream(outputStream);
tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_POSIX);
//read from previous version of tar file
ArchiveEntry entry = tin.getNextEntry();
while (entry != null) {//previous file have entries
String name = entry.getName();
boolean notInFiles = true;
for (File f : flist) {
if (f.getName().equals(name)) {
notInFiles = false;
break;
}
}
if (notInFiles) {
// Add TAR entry to output stream.
if (!entry.isDirectory()) {
tos.putArchiveEntry(new TarArchiveEntry(name));
// Transfer bytes from the TAR file to the output file
int len;
while ((len = tin.read(buf)) > 0) {
tos.write(buf, 0, len);
}
}
}
entry = tin.getNextEntry();
}
// Close the streams
tin.close();//finished reading existing entries
// Compress new files
for (int i = 0; i < flist.length; i++) {
if (flist[i].isDirectory()) {
continue;
}
InputStream fis = new FileInputStream(flist[i]);
TarArchiveEntry te = new TarArchiveEntry(flist[i],flist[i].getName());
//te.setSize(flist[i].length());
tos.setLongFileMode(TarArchiveOutputStream.LONGFILE_GNU);
tos.setBigNumberMode(2);
tos.putArchiveEntry(te); // Add TAR entry to output stream.
// Transfer bytes from the file to the TAR file
int count = 0;
while ((count = fis.read(buf, 0, buffersize)) != -1) {
tos.write(buf, 0, count);
}
tos.closeArchiveEntry();
fis.close();
}
// Complete the TAR file
tos.close();
tempFile.delete();
}
}