I am trying to unzip all the jar files and the jar files which is nested in jar file.
For example, let's say there's a Test.jar and inside of the Test.jar, there is Test1.jar,,
What I tried to do is that making a temp directory and unzip them, when it was jar file, recursive call.
Here below is my code and the log I got. I have no idea on that.
I am pretty sure that the input was directory. I have no idea on resolving this error. Also, I am pretty sure that the error is from here (Collection<File> files = FileUtils.listFiles(root, null, recursive);)
Curr directory:/Users/younghoonkwon/jar-analyzer
unzipping directory/Users/younghoonkwon/jar-analyzer/test1.jar#
Curr directory:/Users/younghoonkwon/jar-analyzer/test1.jar#
java.lang.IllegalArgumentException: Parameter 'directory' is not a directory
at org.apache.commons.io.FileUtils.validateListFilesParameters(FileUtils.java:545)
at org.apache.commons.io.FileUtils.listFiles(FileUtils.java:521)
at org.apache.commons.io.FileUtils.listFiles(FileUtils.java:691)
at org.vulnerability.checker.JarParser.unzipJars(JarParser.java:31)
at org.vulnerability.checker.JarParser.unzipJars(JarParser.java:38)
at org.vulnerability.checker.VulnerabilityChecker.main(VulnerabilityChecker.java:26)
[/Users/younghoonkwon/jar-analyzer/test1.jar]
My code:
public void unzipJars(String toFind, String currDirectory) {
File root = new File(currDirectory);
try {
boolean recursive = true;
System.out.println("Curr directory:"+root);
Collection<File> files = FileUtils.listFiles(root, null, recursive);
for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) {
File file = (File) iterator.next();
if (file.getName().endsWith(toFind)) {
if(toFind.endsWith("jar")) {
unzip(file.getAbsolutePath() + "#",file.getAbsolutePath());
System.out.println("unzipping directory"+ file.getAbsolutePath()+"#");
unzipJars("jar", file.getAbsolutePath()+"#");
this.jarList.add(file.getAbsolutePath());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
static void unzip(String destDirPath, String zipFilePath) throws IOException {
Runtime.getRuntime().exec("unzip "+ zipFilePath + " -d" + destDirPath);
}
The algorithm seems ok to me. The error seems to be caused by that the unzipped file is not a directory (but a file) or it does not exist. Your unzip() method does not throw any exeption if unzipping the .jar fails because of output file already existing.
Have you been running the code earlier which may have caused that the .jars or directories contain unwanted output files with the same name?
Before the call to FileUtils.listFiles(), check if the root File object is actually a directory and it exists (or if it's a file but not a directory) by File.isDirectory() or File.isFile().
The following method decompress() unzips a JAR file and all the JAR files within it (recursively).
/**
* Size of the buffer to read/write data.
*/
private static final int BUFFER_SIZE = 16384;
/**
* Decompress all JAR files located in a given directory.
*
* #param outputDirectory Path to the directory where the decompressed JAR files are located.
*/
public static void decompress(final String outputDirectory) {
File files = new File(outputDirectory);
for (File f : Objects.requireNonNull(files.listFiles())) {
if (f.getName().endsWith(".jar")) {
try {
JarUtils.decompressDependencyFiles(f.getAbsolutePath());
// delete the original dependency jar file
org.apache.commons.io.FileUtils.forceDelete(f);
} catch (IOException e) {
log.warn("Problem decompressing jar file: " + f.getAbsolutePath());
}
}
}
}
/**
* Decompress all JAR files (recursively).
*
* #param zipFile The file to be decompressed.
*/
private static void decompressDependencyFiles(String zipFile) throws IOException {
File file = new File(zipFile);
try (ZipFile zip = new ZipFile(file)) {
String newPath = zipFile.substring(0, zipFile.length() - 4);
new File(newPath).mkdir();
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
// Process each entry
while (zipFileEntries.hasMoreElements()) {
// grab a zip file entry
ZipEntry entry = zipFileEntries.nextElement();
String currentEntry = entry.getName();
File destFile = new File(newPath, currentEntry);
File destinationParent = destFile.getParentFile();
// create the parent directory structure if needed
destinationParent.mkdirs();
if (!entry.isDirectory()) {
BufferedInputStream is = new BufferedInputStream(zip.getInputStream(entry));
int currentByte;
// establish buffer for writing file
byte[] data = new byte[BUFFER_SIZE];
// write the current file to disk
FileOutputStream fos = new FileOutputStream(destFile);
try (BufferedOutputStream dest = new BufferedOutputStream(fos, BUFFER_SIZE)) {
// read and write until last byte is encountered
while ((currentByte = is.read(data, 0, BUFFER_SIZE)) != -1) {
dest.write(data, 0, currentByte);
}
dest.flush();
is.close();
}
}
if (currentEntry.endsWith(".jar")) {
// found a zip file, try to open
decompressDependencyFiles(destFile.getAbsolutePath());
FileUtils.forceDelete(new File(destFile.getAbsolutePath()));
}
}
}
}
Related
package com.example.demo.Util;
public class Test {
static HashMap<String,String> map = new HashMap<>();
public static void main(String[] args) throws IOException {
String data = "12j3h1i7tsa7sgdajk123y8asd: 88888";
File jarFile = new File(new Test().getJarPath());
File tempJar = upJarFile(jarFile, "BOOT-INF/classes/application.properties", data);
}
public static File upJarFile(File originalJarFile, String editFilePath, String content) throws IOException {
File tempFile = File.createTempFile("temp", ".jar");
JarFile jarFile = new JarFile(originalJarFile);
Enumeration<JarEntry> entries = jarFile.entries();
System.out.println("before:"+ originalJarFile.length());
JarOutputStream jarOutputStream = new JarOutputStream(new FileOutputStream(tempFile));
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
jarOutputStream.putNextEntry(jarEntry);
map.put(jarEntry.getName(), String.valueOf(jarEntry.getSize()));
jarOutputStream.write(new Test().inputStreamToByteArray(jarFile.getInputStream(jarEntry)));
}
jarOutputStream.finish();
jarOutputStream.close();
System.out.println(tempFile.getPath());
System.out.println("after:" + tempFile.length());
return tempFile;
}
public String getJarPath() {
String path1 = System.getProperty("user.dir");
File file = new File(path1 + "/target/");
String jarFile = null;
for (File file1 : file.listFiles()) {
if (file1.getName().endsWith(".jar")) {
jarFile = file1.getPath();
break;
}
}
return jarFile;
}
public byte[] inputStreamToByteArray(InputStream inputStream) {
try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int num;
while ((num = inputStream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, num);
}
byteArrayOutputStream.flush();
return byteArrayOutputStream.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return new byte[]{};
}
}
As shown in the code above,I just turn the incoming jar packages into streams and write them one by one,But it got smaller when I tested the size of the input package and the size of the output temporary package(before:49651057-->after:49647985)
What could be causing this difference?
This can happen due to a number of reasons:
The original JAR file was created with a compression level that is not as high as the default compression level, so the JAR file that you create (with default compression) achieves better compression, and therefore it is smaller. You can verify this by opening both the original and the result JAR files with a ZIP utility (e.g. 7Zip) and examining their checksums and their compressed sizes. If the checksums are identical, but the compressed sizes differ, then the difference is simply due to better compression.
The original JAR file contains unused data. This can happen when sloppy archive creation software updates an archive by appending to it instead of rewriting it from scratch. You can verify this by opening the original ZIP archive with a ZIP utility (e.g. 7Zip) and saving it under a new filename. If the new file is smaller, then the original file contained some unused data.
The original JAR file contains files in subdirectories, which you are not checking. Thus, your output JAR file does not contain all of the files in the original. To fix this, you need to check each entry with jarEntry.isDirectory() and if so, recurse.
I am trying to unzip a file from sdcard using below code
public void unzip(String zipFilePath, String destDirectory, String filename) throws IOException {
File destDir = new File(destDirectory);
ZipInputStream zipIn = new ZipInputStream(new FileInputStream(zipFilePath));
ZipEntry entry = zipIn.getNextEntry();
// iterates over entries in the zip file
while (entry != null) {
String filePath = destDirectory + File.separator + entry.getName();
if (!entry.isDirectory()) {
// if the entry is a file, extracts it
extractFile(zipIn, filePath);
} else {
// if the entry is a directory, make the directory ;
File dir = new File(filename);
dir.mkdir();
}
zipIn.closeEntry();
entry = zipIn.getNextEntry();
}
zipIn.close();
}
/**
* Extracts a zip entry (file entry)
* #param zipIn
* #param filePath
* #throws IOException
*/
private void extractFile(ZipInputStream zipIn, String filePath) throws IOException {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(filePath));
byte[] bytesIn = new byte[BUFFER_SIZE];
int read = 0;
while ((read = zipIn.read(bytesIn)) != -1) {
bos.write(bytesIn, 0, read);
}
bos.close();
}
The above code is giving me errors. below are the logs
java.io.FileNotFoundException: /mnt/sdcard/unZipedFiles/myfile/tt/images.jpg: open failed: ENOENT (No such file or directory)
Here I ziped directory which contains images/sub-directory, then I am trying to unzip.
Can anybody tell me the reasons
Thanks
You are trying to write files to a directory that does not exist. This will not work. Not only do you need to create the files when unZIPping, you need to create the directories as well.
Add the following to extractPath() as its opening line:
filePath.getParentFile().mkdirs();
This gets the directory that should contain your desired file (filePath.getParentFile()), then creates all necessary subdirectories to get there (mkdirs()).
I'm having the problem of replacing or updating some files within a certain directory inside a jar file.
I've read a few post already. The code (the JarUpdater Class) given at this link Updating .JAR's contents from code
is being very helpful for me to understand the role and the use of ZipInputStream, ZipOutputStream and ZipEntry, etc..
However, when I run it,
I have an EOF Exception
[EDITED by mk7: I found out the jar file was corrupted after I went through it 20 times or so. So after I replaced the jar file with a new one, the EOF Exception went away. The other two problems below still remains unsolved]
these two new xml files only get copied to the "root directory" of the jar file.
these two new xml files NEVER replaced the two original files inside a directory called /conf.
Which lines of code should I change in order to replace the xml files with the new ones?
With the System.out.println, I did see that the while loop steps through every directory and compare at every file as expected. A new temp jar was also created as expected...
I thought the statement "notInFiles = false" would take care of my need but it's NOT.
How do I step into the /conf and only replace those two files and NOT leave a copy at the root of the jar file?
What am I missing? Thanks for any insight!
Below are the code from that link.
import java.io.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class JarUpdater {
public static void main(String[] args) {
File[] contents = {new File("abc.xml"),
new File("def.xml")};
File jarFile = new File("xyz.jar");
try {
updateZipFile(jarFile, contents);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void updateZipFile(File jarFile,
File[] contents) throws IOException {
// get a temp file
File tempFile = File.createTempFile(jarFile.getName(), null);
// delete it, otherwise you cannot rename your existing zip to it.
tempFile.delete();
System.out.println("tempFile is " + tempFile);
boolean renameOk=jarFile.renameTo(tempFile);
if (!renameOk)
{
throw new RuntimeException("could not rename the file "+jarFile.getAbsolutePath()+" to "+tempFile.getAbsolutePath());
}
byte[] buf = new byte[1024];
ZipInputStream zin = new ZipInputStream(new FileInputStream(tempFile));
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(jarFile));
ZipEntry entry = zin.getNextEntry();
while (entry != null) {
String name = entry.getName();
boolean notInFiles = true;
for (File f : contents) {
System.out.println("f is " + f);
if (f.getName().equals(name)) {
// that file is already inside the jar file
notInFiles = false;
System.out.println("file already inside the jar file");
break;
}
}
if (notInFiles) {
System.out.println("name is " + name);
System.out.println("entry is " + entry);
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(name));
// Transfer bytes from the ZIP file to the output file
int len;
while ((len = zin.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
entry = zin.getNextEntry();
}
// Close the streams
zin.close();
// Compress the contents
for (int i = 0; i < contents.length; i++) {
InputStream in = new FileInputStream(contents[i]);
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(contents[i].getName()));
// Transfer bytes from the file to the ZIP file
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
// Complete the entry
out.closeEntry();
in.close();
}
// Complete the ZIP file
out.close();
tempFile.delete();
}
}
In your first cycle (while loop) where you copy the entries which you don't want to replace you don't close the entries in the output zip file. Add out.closeEntry(); like this:
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry(name));
// Transfer bytes from the ZIP file to the output file
int len;
while ((len = zin.read(buf)) > 0) {
out.write(buf, 0, len);
}
// ADD THIS LINE:
out.closeEntry();
Also when you check if an entry is to be replaced, you should compare it to a full path, not just to the name of the file. For example if you want to replace abc.xml which is in the /conf folder, you should compare the entry name to "/conf/abc.xml" and not to "abc.xml".
To properly check if an entry is to be replaced:
String name = entry.getName();
boolean notInFiles = true;
for (File f : contents) {
System.out.println("f is " + f);
if (name.equals("/conf/" + f.getName()) {
// that file is already inside the jar file
notInFiles = false;
System.out.println("file already inside the jar file");
break;
}
}
And when you add the entries to the output which are the replaced files, you also have to specify the entry name having full path, e.g. "/conf/abc.xml" and not just "abc.xml" because it would put "abc.xml" in the root of the output zip.
To do this, start the entry name with "/conf/" like this:
// Add ZIP entry to output stream.
out.putNextEntry(new ZipEntry("/conf/" + contents[i].getName()));
For URIs with the protocol jar:file: (usable for all zip files), you can use a zip file system.
URI jarUri = new URI("jar:" + jarFile.toURI().toString()); // "jar:file:/C:/../xyz.jar"
Map<String, String> zipProperties = new HashMap<>();
zipProperties.put("encoding", "UTF-8");
try (FileSystem zipFS = FileSystems.newFileSystem(jarUri, zipProperties)) {
for (File file : contents) {
Path updatePath = zipFS.getPath("/" + file.getName());
Files.delete(updatePath);
Files.copy(file.toPath(), updatePath, StandardCopyOption.REPLACE_EXISTING);
}
} // closes.
One way to derive the URI is prefixing "jar:" to a File.toURI().
This is a bit more elegant and abstract, and also allows Files.copy in and out the zip. Something to keep in ones tool chest.
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();
}
}
}
}
I am working with some file manipulations, just simple read and writes using the java APIs File object.
so here is what I do. first the application receives a zip file, then it extracts the contents on a TEMPFOLDER. And then another class, totally independent, will work on the TEMPFOLDER. It will check the number of files and it should count them all. then it will do stuff to perform some database functions to write the contents of the zip file to the DB
but here is the problem, when I use the application for the first time it goes well smoothly. then WHEN I DO IT THE SECOND TIME AROUND it will fail because the second class which is supposed to check the TEMPFOLDER return 0 as the number of files in the said folder but when I check it manually, it has some contents.
and the pattern it does when I test it continously, it will work, then it will not work, it will work, then it will not work. it acts like that. the reason it does not work is because the application cannot determine correctly the number of files in a folder. it has files but the file object is returning that it has no items in it. but after the error, if you run it again it will work as it is supposed to work.
if you could give out some suggestions from my explanation which is a thousand feet view, i would appreciate it and try it for my debugging. but if you would need some codes, i will post them later
by the way, I am using a web browser and a servlet to accept the zip file
here is the method that i used to write to file system.
public void extractZipAndWriteContentsToFileSystem(ZipFile zipFile) throws IOException, Exception {
Enumeration en = zipFile.entries();
while (en.hasMoreElements()) {
ZipEntry zipEntry = (ZipEntry) en.nextElement();
String name = zipEntry.getName();
long size = zipEntry.getSize();
long compressedSize = zipEntry.getCompressedSize();
File file = new File(zipExtractsTempRepo+name);
if (name.endsWith("/")) {
file.mkdirs();
continue;
}
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
InputStream is = zipFile.getInputStream(zipEntry);
FileOutputStream fos = new FileOutputStream(file);
byte[] bytes = new byte[1024];
int length;
while ((length = is.read(bytes)) >= 0) {
fos.write(bytes, 0, length);
}
file = null;
parent = null;
is.close();
fos.close();
}
zipFile.close();
traverse(new File(zipExtractsTempRepo));
}
/**
* this method traverses through a folder and its subfolders
* and its subfolders and ...
*
* this method retrieves objects that are files. if it is not
* a file, (then a directory) it looks inside it to look
* for other files
*
* #param file
* #throws IOException
*/
public void traverse(File file) throws IOException {
if (file.isDirectory()) {
File[] allFiles = file.listFiles();
for (File aFile : allFiles) {
traverse(aFile);
}
} else {
FileInputStream in = new FileInputStream(file);
FileOutputStream out = new FileOutputStream(
new File(this.tempRepo + file.getName()));
byte[] bytes = new byte[1024];
int length;
while ((length = in.read(bytes)) >= 0) {
out.write(bytes, 0, length);
}
out.write(4024);
in.close();
out.close();
}
}
now this is the method on the second class to verify the number of file written on a folder
private File[] files;
private File[] zipExtracts;
// location of the tempfolder
tempFolder = new File(RawZipFileHandler.tempRepo);
files = tempFolder.listFiles();
public void checkNumberOfFiles() throws Exception {
Util.out("will check number of files");
// check the number of documents on the tempfile folder
if (files.length != 8) {
throw new Exception ("Number of files expected not met. Expected is 8 got " + files.length);
}
}
comments for my not so pretty code are also welcomed.