Java Jar file: use resource errors: URI is not hierarchical - java

I have deployed my app to jar file. When I need to copy data from one file of resource to outside of jar file, I do this code:
URL resourceUrl = getClass().getResource("/resource/data.sav");
File src = new File(resourceUrl.toURI()); //ERROR HERE
File dst = new File(CurrentPath()+"data.sav"); //CurrentPath: path of jar file don't include jar file name
FileInputStream in = new FileInputStream(src);
FileOutputStream out = new FileOutputStream(dst);
// some excute code here
The error I have met is: URI is not hierarchical. this error I don't meet when run in IDE.
If I change above code as some help on other post on StackOverFlow:
InputStream in = Model.class.getClassLoader().getResourceAsStream("/resource/data.sav");
File dst = new File(CurrentPath() + "data.sav");
FileOutputStream out = new FileOutputStream(dst);
//....
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) { //NULL POINTER EXCEPTION
//....
}

You cannot do this
File src = new File(resourceUrl.toURI()); //ERROR HERE
it is not a file!
When you run from the ide you don't have any error, because you don't run a jar file. In the IDE classes and resources are extracted on the file system.
But you can open an InputStream in this way:
InputStream in = Model.class.getClassLoader().getResourceAsStream("/data.sav");
Remove "/resource". Generally the IDEs separates on file system classes and resources. But when the jar is created they are put all together. So the folder level "/resource" is used only for classes and resources separation.
When you get a resource from classloader you have to specify the path that the resource has inside the jar, that is the real package hierarchy.

If for some reason you really need to create a java.io.File object to point to a resource inside of a Jar file, the answer is here: https://stackoverflow.com/a/27149287/155167
File f = new File(getClass().getResource("/MyResource").toExternalForm());

Here is a solution for Eclipse RCP / Plugin developers:
Bundle bundle = Platform.getBundle("resource_from_some_plugin");
URL fileURL = bundle.getEntry("files/test.txt");
File file = null;
try {
URL resolvedFileURL = FileLocator.toFileURL(fileURL);
// We need to use the 3-arg constructor of URI in order to properly escape file system chars
URI resolvedURI = new URI(resolvedFileURL.getProtocol(), resolvedFileURL.getPath(), null);
File file = new File(resolvedURI);
} catch (URISyntaxException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
It's very important to use FileLocator.toFileURL(fileURL) rather than resolve(fileURL)
, cause when the plugin is packed into a jar this will cause Eclipse to create an unpacked version in a temporary location so that the object can be accessed using File. For instance, I guess Lars Vogel has an error in his article - http://blog.vogella.com/2010/07/06/reading-resources-from-plugin/

I got a similiar issues before, and I used the code:
new File(new URI(url.toString().replace(" ","%20")).getSchemeSpecificPart());
instead of the code :
new File(new URI(url.toURI())
to solve the problem

While I stumbled upon this problem myself I'd like to add another option (to the otherwise perfect explanation from #dash1e):
Export the plugin as a folder (not a jar) by adding:
Eclipse-BundleShape: dir
to your MANIFEST.MF.
At least when you export your RCP app with the export wizard (based on a *.product) file this gets respected and will produce a folder.

In addition to the general answers, you can get "URI is not hierarchical" from Unitils library attempting to load a dataset off a .jar file. It may happen when you keep datasets in one maven submodule, but actual tests in another.
There is even a bug UNI-197 filed.

Related

Java inputstream from a relative path [duplicate]

Is there a way in Java to construct a File instance on a resource retrieved from a jar through the classloader?
My application uses some files from the jar (default) or from a filesystem directory specified at runtime (user input). I'm looking for a consistent way of
a) loading these files as a stream
b) listing the files in the user-defined directory or the directory in the jar respectively
Edit: Apparently, the ideal approach would be to stay away from java.io.File altogether. Is there a way to load a directory from the classpath and list its contents (files/entities contained in it)?
I had the same problem and was able to use the following:
// Load the directory as a resource
URL dir_url = ClassLoader.getSystemResource(dir_path);
// Turn the resource into a File object
File dir = new File(dir_url.toURI());
// List the directory
String files = dir.list()
ClassLoader.getResourceAsStream and Class.getResourceAsStream are definitely the way to go for loading the resource data. However, I don't believe there's any way of "listing" the contents of an element of the classpath.
In some cases this may be simply impossible - for instance, a ClassLoader could generate data on the fly, based on what resource name it's asked for. If you look at the ClassLoader API (which is basically what the classpath mechanism works through) you'll see there isn't anything to do what you want.
If you know you've actually got a jar file, you could load that with ZipInputStream to find out what's available. It will mean you'll have different code for directories and jar files though.
One alternative, if the files are created separately first, is to include a sort of manifest file containing the list of available resources. Bundle that in the jar file or include it in the file system as a file, and load it before offering the user a choice of resources.
Here is a bit of code from one of my applications...
Let me know if it suits your needs.
You can use this if you know the file you want to use.
URL defaultImage = ClassA.class.getResource("/packageA/subPackage/image-name.png");
File imageFile = new File(defaultImage.toURI());
A reliable way to construct a File instance on a resource retrieved from a jar is it to copy the resource as a stream into a temporary File (the temp file will be deleted when the JVM exits):
public static File getResourceAsFile(String resourcePath) {
try {
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
if (in == null) {
return null;
}
File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
tempFile.deleteOnExit();
try (FileOutputStream out = new FileOutputStream(tempFile)) {
//copy stream
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
return tempFile;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
Try this:
ClassLoader.getResourceAsStream ("some/pkg/resource.properties");
There are more methods available, e.g. see here:
http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html
This is one option: http://www.uofr.net/~greg/java/get-resource-listing.html

store file in spring boot resource folder after deployment

I have deployed a spring-boot application JAR file. Now, I want to upload the image from android and store it in the myfolder of resource directory. But unable to get the path of resource directory.
Error is:
java.io.FileNotFoundException: src/main/resources/static/myfolder/myimage.png
(No such file or directory)
This is the code for storing the file in the resource folder
private final String RESOURCE_PATH = "src/main/resources";
String filepath = "/myfolder/";
public String saveFile(byte[] bytes, String filepath, String filename) throws MalformedURLException, IOException {
File file = new File(RESOURCE_PATH + filepath + filename);
OutputStream out = new FileOutputStream(file);
try {
out.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
out.close();
}
return file.getName();
}
UPDATED:
This is what I have tried
private final String RESOURCE_PATH = "config/";
controller class:
String filepath = "myfolder/";
String filename = "newfile.png"
public String saveFile(byte[] bytes, String filepath, String filename) throws MalformedURLException, IOException {
//reading old file
System.out.println(Files.readAllBytes(Paths.get("config","myfolder","oldfile.png"))); //gives noSuchFileException
//writing new file
File file = new File(RESOURCE_PATH + filepath + filename);
OutputStream out = new FileOutputStream(file); //FileNotFoundException
try {
out.write(bytes);
} catch (Exception e) {
e.printStackTrace();
} finally {
out.close();
}
return file.getName();
}
Project structure:
+springdemo-0.0.1-application.zip
+config
+myfolder
-oldfile.png
-application.properties
+lib
+springdemo-0.0.1.jar
+start.sh
-springdemo-0.0.1.jar //running this jar file
Usually when you deploy an application (or start it using Java), you start a JAR file. You don't have a resource folder. You can have one and access it, too, but it certainly won't be src/main/resources.
When you build your final artifact (your application), it creates a JAR (or EAR or WAR) file and your resources, which you had in your src/main/resources-folder, are copied over to the output directory and included in the final artifact. That folder simply does not exist when the application is run (assuming you are trying to run it standalone).
During the build process target/ is created and contains the classes, resources, test-resources and the likes (assuming you are building with Maven; it is a little different if you build using Gradle or Ant or by hand).
What you can do is create a folder e.g. docs next to your final artifact, give it the appropriate permissions (chmod/chown) and have your application output files into that folder. This folder is then expected to exist on the target machine running your artifact, too, so if it doesn't, it would mean the folder does not exist or the application lacks the proper permissions to read from / write to that folder.
If you need more details, don't hesitate to ask.
Update:
To access a resource, which is bundled and hence inside your artifact (e.g. final.jar), you should be able to retrieve it by using e.g. the following:
testText = new String(ControllerClass.class.getResourceAsStream("/test.txt").readAllBytes());
This is assuming your test.txt file is right under src/main/resources and was bundled to be directly in the root of your JAR-file (or target folder where your application is run from). ControllerClass is the controller, which is accessing the file. readAllBytes just does exactly this: read all the bytes from a text file. For accessing images inside your artifact, you might want to use ImageIO.
IF you however want to access an external file, which is not bundled and hence not inside your artifact, you may use File image = new File(...) where ... would be something like "docs/image.png". This would require you to create a folder called docs next to your JAR-artifact and put a file image.png inside of it.
You of course also may work with streams and there are various helpful libraries for working with input- and output streams.
The following was meant for AWT, but it works in case you really want to access the bytes of your image: ImageIO. In a controller you usually wouldn't want to do that, but rather have your users access (and thus download) it from a given available folder.
I hope this helps :).

Unable to read resources from .jar [duplicate]

This question already has answers here:
Java Jar file: use resource errors: URI is not hierarchical
(6 answers)
Closed 6 years ago.
I have files in resource folder. For example if I need to get file from resource folder, I do like that:
File myFile= new File(MyClass.class.getResource(/myFile.jpg).toURI());
System.out.println(MyClass.class.getResource(/myFile.jpg).getPath());
I've tested and everything works!
The path is
/D:/java/projects/.../classes/X/Y/Z/myFile.jpg
But, If I create jar file, using , Maven:
mvn package
...and then start my app:
java -jar MyJar.jar
I have that following error:
Exception in thread "Thread-4" java.lang.RuntimeException: ხელმოწერის განხორციელება შეუძლებელია
Caused by: java.lang.IllegalArgumentException: URI is not hierarchical
at java.io.File.<init>(File.java:363)
...and path of file is:
file:/D:/java/projects/.../target/MyJar.jar!/X/Y/Z/myFile.jpg
This exception happens when I try to get file from resource folder. At this line. Why? Why have that problem in JAR file? What do you think?
Is there another way, to get the resource folder path?
You should be using
getResourceAsStream(...);
when the resource is bundled as a jar/war or any other single file package for that matter.
See the thing is, a jar is a single file (kind of like a zip file) holding lots of files together. From Os's pov, its a single file and if you want to access a part of the file(your image file) you must use it as a stream.
Documentation
Here is a solution for Eclipse RCP / Plugin developers:
Bundle bundle = Platform.getBundle("resource_from_some_plugin");
URL fileURL = bundle.getEntry("files/test.txt");
File file = null;
try {
URL resolvedFileURL = FileLocator.toFileURL(fileURL);
// We need to use the 3-arg constructor of URI in order to properly escape file system chars
URI resolvedURI = new URI(resolvedFileURL.getProtocol(), resolvedFileURL.getPath(), null);
File file = new File(resolvedURI);
} catch (URISyntaxException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
It's very important to use FileLocator.toFileURL(fileURL) rather than resolve(fileURL)
, cause when the plugin is packed into a jar this will cause Eclipse to create an unpacked version in a temporary location so that the object can be accessed using File. For instance, I guess Lars Vogel has an error in his article - http://blog.vogella.com/2010/07/06/reading-resources-from-plugin/
I face same issue when I was working on a project in my company. First Of All, The URI is not hierarichal Issue is because probably you are using "/" as file separator.
You must remember that "/" is for Windows and from OS to OS it changes, It may be different in Linux. Hence Use File.seperator .
So using
this.getClass().getClassLoader().getResource("res"+File.separator+"secondFolder")
may remove the URI not hierarichal. But Now you may face a Null Pointer Exception. I tried many different ways and then used JarEntries Class to solve it.
File jarFile = new File(this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
String actualFile = jarFile.getParentFile().getAbsolutePath()+File.separator+"Name_Of_Jar_File.jar";
System.out.println("jarFile is : "+jarFile.getAbsolutePath());
System.out.println("actulaFilePath is : "+actualFile);
final JarFile jar = new JarFile(actualFile);
final Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
System.out.println("Reading entries in jar file ");
while(entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
final String name = jarEntry.getName();
if (name.startsWith("Might Specify a folder name you are searching for")) { //filter according to the path
System.out.println("file name is "+name);
System.out.println("is directory : "+jarEntry.isDirectory());
File scriptsFile = new File(name);
System.out.println("file names are : "+scriptsFile.getAbsolutePath());
}
}
jar.close();
You have to specify the jar name here explicitly. So Use this code, this will give you directory and sub directory inside the folder in jar.

java.lang.NullPointerException while getting a filepath in Java (executing through a jar file)

I am getting an NPE at the point of getting path of a File (an sh file in assets folder).
I have tried to read about NPE i detail from the following thread, but this actually could not solve my problem.
What is a NullPointerException, and how do I fix it?
Following is my code snippet:
File absPathofBash;
url = ClassLoader.class.getResource("assets/forbackingup.sh");
absPathofBash = new File(url.getPath());
Later I'm using it in a ProcessBuilder, as
ProcessBuilder pb = new ProcessBuilder(url.getPath(), param2, param3)
I've also tried getting the absolute path directly, like
absPathofBash = new File("assets/forbackingup.sh").getAbsolutePath();
Using the latter way, I am able to process it, but if I create a jar then the file cannot be found. (although the Jar contains the file within the respective folder assets)
I would be thankful if anyone can help me on that.
Once you have packaged your code as a jar, you can not load files that are inside the jar using file path, instead they are class resources and you have to use this to load:
this.getClass().getClassLoader().getResource("assets/forbackingup.sh");
This way you load assets/forbackingup.sh as an absolute path inside your jar. you also can use this.getClass().getResource() but this way the path must be relative to this class path inside jar.
getResource method gives you an URL, if you want to get directly an InputStream you can use getResourceAsStream
Hope it helps!
Since the file itself is in the jar file, you could try using:
InputStream is = this.getClass().getClassLoader().getResourceAsStream(fileNameFromJar);
In case of jar file , classloader will return URL different than that of when the target file is not embedded inside jar. Refer to answer on link which should help u :
How to use ClassLoader.getResources() in jar file
I got it done by creating a temp file. Though it's not difficult, yet I'm posting the code patch here:
InputStream stream = MyClass.class.getClassLoader().
getResourceAsStream("assets/forbackingup.sh");
File temp = File.createTempFile("forbackingup", ".sh");
OutputStream outputStream =
new FileOutputStream(temp);
int read = 0;
byte[] bytes = new byte[1024];
while ((read = stream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
outputStream.close();
}
Now, we have this temp file here which we can pipe to the ProcessBuilder like,
String _filePath=temp.getPath();
ProcessBuilder pb = new ProcessBuilder(url.getPath(), param2, param3)
Thank you everyone for your considerations.
You can use Path class like :
Path path = Paths.get("data/test-write.txt");
if(!Files.exists(path)){
// can handle null pointer exception
}

Java resource as File

Is there a way in Java to construct a File instance on a resource retrieved from a jar through the classloader?
My application uses some files from the jar (default) or from a filesystem directory specified at runtime (user input). I'm looking for a consistent way of
a) loading these files as a stream
b) listing the files in the user-defined directory or the directory in the jar respectively
Edit: Apparently, the ideal approach would be to stay away from java.io.File altogether. Is there a way to load a directory from the classpath and list its contents (files/entities contained in it)?
I had the same problem and was able to use the following:
// Load the directory as a resource
URL dir_url = ClassLoader.getSystemResource(dir_path);
// Turn the resource into a File object
File dir = new File(dir_url.toURI());
// List the directory
String files = dir.list()
ClassLoader.getResourceAsStream and Class.getResourceAsStream are definitely the way to go for loading the resource data. However, I don't believe there's any way of "listing" the contents of an element of the classpath.
In some cases this may be simply impossible - for instance, a ClassLoader could generate data on the fly, based on what resource name it's asked for. If you look at the ClassLoader API (which is basically what the classpath mechanism works through) you'll see there isn't anything to do what you want.
If you know you've actually got a jar file, you could load that with ZipInputStream to find out what's available. It will mean you'll have different code for directories and jar files though.
One alternative, if the files are created separately first, is to include a sort of manifest file containing the list of available resources. Bundle that in the jar file or include it in the file system as a file, and load it before offering the user a choice of resources.
Here is a bit of code from one of my applications...
Let me know if it suits your needs.
You can use this if you know the file you want to use.
URL defaultImage = ClassA.class.getResource("/packageA/subPackage/image-name.png");
File imageFile = new File(defaultImage.toURI());
A reliable way to construct a File instance on a resource retrieved from a jar is it to copy the resource as a stream into a temporary File (the temp file will be deleted when the JVM exits):
public static File getResourceAsFile(String resourcePath) {
try {
InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath);
if (in == null) {
return null;
}
File tempFile = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
tempFile.deleteOnExit();
try (FileOutputStream out = new FileOutputStream(tempFile)) {
//copy stream
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) != -1) {
out.write(buffer, 0, bytesRead);
}
}
return tempFile;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
Try this:
ClassLoader.getResourceAsStream ("some/pkg/resource.properties");
There are more methods available, e.g. see here:
http://www.javaworld.com/javaworld/javaqa/2003-08/01-qa-0808-property.html
This is one option: http://www.uofr.net/~greg/java/get-resource-listing.html

Categories

Resources