OK. So I have a pretty simple question: I want to be able to load a resource (a whole folder) from inside a running .jar file, but I have not been able to get it to work. This is what I have tried (if the class name were "myClass" and the folder being called "myFolder"), but it always throws a NullPointerException:
URL folderURL = myClass.class.getClassLoader().getResource("myFolder/");
String folderPath = folderURL.getPath();
File myFolder = new File(folderPath);
The NullPointerException is always thrown before I create "myFolder".
Some more info: I have to access the folder from static context. The class that is accessing the folder is NOT in the same directory as the folder itself is in. (The folder is in the root directory inside the jar, the class is a couple subpackages down.)
Does anyone have a solution to my problem? Sorry if I used wrong terminology :P, but anything you can do to help is appreciated.
There's no way this will work. You're trying to create a File object from a resource inside a JAR. That isn't going to happen. The best method to load resources is to make one your package folders a resource folder, then make a Resources.jar in it or something, dump your resources in the same dir, and then use Resources.class.getResourceAsStream(resFileName) in your other Java class files.
If you need to 'brute force' the subfiles in the JAR directory pointed to by the URL given by getResource(..), use the following (although it's a bit of a hack!). It will work for a normal filesystem too:
/**
* List directory contents for a resource folder. Not recursive.
* This is basically a brute-force implementation.
* Works for regular files and also JARs.
*
* #author Greg Briggs
* #param clazz Any java class that lives in the same place as the resources you want.
* #param path Should end with "/", but not start with one.
* #return Just the name of each member item, not the full paths.
* #throws URISyntaxException
* #throws IOException
*/
String[] getResourceListing(Class clazz, String path) throws URISyntaxException, IOException {
URL dirURL = clazz.getClassLoader().getResource(path);
if (dirURL != null && dirURL.getProtocol().equals("file")) {
/* A file path: easy enough */
return new File(dirURL.toURI()).list();
}
if (dirURL == null) {
/*
* In case of a jar file, we can't actually find a directory.
* Have to assume the same jar as clazz.
*/
String me = clazz.getName().replace(".", "/")+".class";
dirURL = clazz.getClassLoader().getResource(me);
}
if (dirURL.getProtocol().equals("jar")) {
/* A JAR path */
String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); //strip out only the JAR file
JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
Set<String> result = new HashSet<String>(); //avoid duplicates in case it is a subdirectory
while(entries.hasMoreElements()) {
String name = entries.nextElement().getName();
if (name.startsWith(path)) { //filter according to the path
String entry = name.substring(path.length());
int checkSubdir = entry.indexOf("/");
if (checkSubdir >= 0) {
// if it is a subdirectory, we just return the directory name
entry = entry.substring(0, checkSubdir);
}
result.add(entry);
}
}
return result.toArray(new String[result.size()]);
}
throw new UnsupportedOperationException("Cannot list files for URL "+dirURL);
}
You can then modify the URL given by getResource(..) and append the file on the end, and pass these URLs into getResourceAsStream(..), ready for loading. If you didn't understand this, you need to read up on classloading.
It's pretty simple:
getClass().getResourceAsStream("/resources/images/myImage.png")
Would return an input stream which can be used like so:
Image myImage = ImageIO.read(getClass().getResourceAsStream("/resources/images/myImage.png"));
And from there, use your Image how you like. This also works just as well if you're using the input stream to read a text file, or for pretty much whatever else you're doing.
Edit:
The path above is relative to the .jar's root.
I think I am on to something. In Eclipse, make a new source folder named "resources" and in that folder, create a package named "myFolder". To have a Path to "myFolder", you just have to say:
Path path = Paths.get(Main.class.getResource("/myFolder").toURI()); // or the class name instead of Main
Up until now (December 2017), this is the only solution I found which works both inside and outside the IDE (Using PathMatchingResourcePatternResolver): https://stackoverflow.com/a/47904173/6792588
Related
In my java application,
1. I have relative path of the directory (Directory and files in it are part of the build).
2.The directory contains multiple files.
3. I want to read the file names in the parent directory.
4. Files can change later and are many in number, So I do not know the names of the files and Do not want my code to change if more files are added or removed or renamed
Now as I do not know the names of the files before hand as they may change later (there are multiple files which can vary according to environment). I only know about the relative path of the parent directory of the files.
How do I read the files ?
You can get list of all files of that directory by file.getlistFiles() method of file class.
It returns an array of files.
Even you can define filter for your files, so it returns exactly files that you want.
try {
File f = new File("D:/Programming");
FilenameFilter filter = new FilenameFilter() {
#Override
public boolean accept(File f, String name) {
// We want to find only .c files
return name.endsWith(".c");
}
};
// Note that this time we are using a File class as an array,
// instead of String
File[] files = f.listFiles(filter);
look at this example.
If you want to use relative path, You can use
System.getProperty("user.dir");
String relative Path =System.getProperty("user.dir");
it returns the folder that you put your app in it.
If your folder has some subfolders, you can simply use file.list();
it returns names of all files and folders of your directory .
String [] listOfMyFilesAndFolders =file.list();
you can add these names to your path to access another folders.
You can check your path is a file or is a folder by using
file.isDirectory();
for ( String path: listOfFilesAndFolders ) {
File file = new File(basePath+path);
if ( file.isDirectory() {
// it is a folder and you can use another for loop or recursion to navigate sub directories
} else {
// it is a file and you can do everyThing you want}}
I think that you can use recursion to walk in your sub directories use recursion to read more
I hope helps.
the question is badly asked and the text is misleading.
"I have relative path of the directory (Directory and files in it are part of the build)" means little and nothing, you have to clarify what you mean.
Assuming you get a relative directory (for example "/folder1/folder2") via command line parameter, you basically have 3 choices:
1) start the program with the directory in which the jar is located as the "current directory", so that it can be used as a working directory. This approach requires that when you launch your application via the java -jar myapp.jar command first you prepend a "cd" command to place yourself directly in the directory. The code will look like the #hamidreza75 solution but obviously you will not have the variable "D: / Programming" but directly the relative path of the directory in which to read the files.
launch script
#echo off
cd {jar-folder}
java -jar myapp.jar
java code:
package com.sample.stack;
import java.io.File;
import java.io.FilenameFilter;
public class FileRenamenter {
public static void main(String[] args) {
final String relativePath = "folder1/folder2";
File directory = new File(relativePath);
String[] list = directory.list(new FilenameFilter(){
public boolean accept(File dir, String name) {
// filter condition
return true;
}
});
// echo file list
for (String filePath : list) {
System.out.println(filePath);
}
}
}
2) pass the folder to be controlled via command line parameter, like this:
launch script
#echo off
java -jar {jar-folder}/myapp.jar {jar-folder}
java code (only the "directory" variable changes)
File directory = new File(args[0], relativePath); // note args[0]
3) programmatically find the folder in which the jar is running [very discouraged practice] and then concatenate the relative path:
java code (only the "directory" variable changes):
String jarFolder = ClassLoader.getSystemClassLoader().getResource(".").getPath();
File directory = new File(jarFolder, relativePath); // note jarFolder
I have jar file langdetect.jar.
It has a hierarchy shown in image
There is a class LanguageDetection at com/langdetect package.
I need to access the path of the profiles.sm folder from above class while executing the jar file.
Thanks in advance.
Jars are nothing else than Zip files and Java provides support for handling those.
Java 6 (and earlier)
You can open the jar file as a ZipFile and iterate over the entries of it. Each entry has a full path name inside the file, there is no such thing as relative path names. Though you have to take care, that all entries - although being absolute in the zip file - do not start with a '/', if you need this, you have to add it. The following snippet will get you the path of a class file. The className has to end with .class, i.e. LanguageDetection.class
String getPath(String jar, String className) throws IOException {
final ZipFile zf = new ZipFile(jar);
try {
for (ZipEntry ze : Collections.list(zf.entries())) {
final String path = ze.getName();
if (path.endsWith(className)) {
final StringBuilder buf = new StringBuilder(path);
buf.delete(path.lastIndexOf('/'), path.length()); //removes the name of the class to get the path only
if (!path.startsWith("/")) { //you may omit this part if leading / is not required
buf.insert(0, '/');
}
return buf.toString();
}
}
} finally {
zf.close();
}
return null;
}
Java 7/8
You may open the JAR file using the Java7 FileSystem support for JAR files. This allows you to operate on the jar file as if it would be normal FileSystem. So you could walk the fileTree until you have found your file and the get the Path from it. The following example uses Java8 Streams and Lambdas, a version for Java7 could be derived from this but would be a bit larger.
Path jarFile = ...;
Map<String, String> env = new HashMap<String, String>() {{
put("create", "false");
}};
try(FileSystem zipFs = newFileSystem(URI.create("jar:" + jarFileFile.toUri()), env)) {
Optional<Path> path = Files.walk(zipFs.getPath("/"))
.filter(p -> p.getFileName().toString().startsWith("LanguageDetection"))
.map(Path::getParent)
.findFirst();
path.ifPresent(System.out::println);
}
Your particular Problem
The above solutions are for finding the path inside a Jar or Zip, but may possibly not be the solution to your problem.
Im not sure, whether I understand your problem correctly. As far as I see it, you'd like to have access to the path inside the classfolder for any purpose. The problem with that is, that the Class/Resource lookup mechanism doesn't apply to folders, only files. The concept that is close is a package, but that is always bound to a class.
So you always need a concrete file to be accessed via getResource() method. For example MyClass.class.getResource(/path/to/resource.txt).
If the resources are located in a profiles.sm folder relative to a class and its package, i.e. in /com/languagedetect/profile.sm/ you could build the path from the reference class, for example the class LanguageDetection in that package and derive the absolute path from this to the profiles.sm path:
String basePath = "/" + LanguageDetection.class.getPackage().getName().replaceAll("\\.", "/") + "/profiles.sm/";
URL resource = LanguageDetection.class.getResource(basePath + "myResource.txt");
If there is only one profiles.sm in the root of the jar, simply go for
String basePath = "/profiles.sm/";
URL resource = LanguageDetection.class.getResource(basePath + "myResource.txt");
If you have multiple jars with a resource in /profiles.sm, you could gain access to all of those via the classloader and then extract the Jar file from the URL of the class
for(URL u : Collections.list(LanguageDetection.class.getClassLoader().getResources("/profiles.sm/yourResource"))){
System.out.println(u);
}
In any case it's not possible without accessing the zip/jar file to browse the contents of this path or folder because Java does not support browsing for classes or resources inside a package/folder in classpath. You may use the Reflections lib for that or extend the ClassLoader example above by additionally reading the content of the detected jars using the zip example from above.
I would like to list files contained into assets subdirectory called "subDir" and am using following code. However, if I set dirFrom = "" (empty), lists all folders in assets properly. However, for dirFrom = "/subDir/", doesn't work. I already tried "subDir/" and same result. Is necessary to declare permissions in manifest? Thank you.
private void copyFiles(String dirFrom, String dirTo) throws IOException {
AssetManager am = getAssets();
String fileList[] = am.list(dirFrom);
if (fileList != null)
{
for ( int i = 0;i<fileList.length;i++)
{
Log.d("",fileList[i]);
}
}
}
AssetManager.list(String) takes a "relative path within the assets".
Android / Java usually expects paths to have no '/' in the end.
Also "relative" means that there should be no '/' at the start - it could otherwise be a path in the root of your filesystem.
Using just "subDir" or "subDir/subsubDir" will work.
zapl´s answer is true, but additional info: If You want to copy the files, for example to sd card, You have to declare Your InputStream with a slash:
InputStream in = assetManager.open("subDir/"+filename);
This was something I have stumbled by copy files from a subfolder. My originally plan was to copy files directly from asset folder, this worked but gave me some IOExceptions. There were some directories "webkit","sound","images" and another one I can´t remember, inside the asset folder...but I have not seen them. This must be something Android internal that I don´t understand. But to get rid of this, I placed my files inside a subfolder. I know it is an old thread, but I put this information to this thread only to help others with similar problems.
I have a project where I generate lots of code against many XSD. To keep things separate each set of XSD are bundled together within a project. I have multiple project that will see XSD in resources and generate code against them.
My problem is when I try to access the XSD that are stored in the jar files I cannot get the code to access the XSD from a perticular jar. Instead it will access the first XSD that matches the criterion regardless of the jar.
Here is the code I use to list the ressources, All the jars have the same structure meaning the XSDs are always stored in the xsd folder at the root of the jar file. The code below lists the XSD in the folder.
URL dirURL = clazz.getClassLoader().getResource(path);
System.out.println(dirURL.toURI());
if (dirURL != null && dirURL.getProtocol().equals("file")) {
/* A file path: easy enough */
System.out.println(dirURL.toURI());
return new File(dirURL.toURI()).list();
}
if (dirURL == null) {
/*
* In case of a jar file, we can't actually find a directory.
* Have to assume the same jar as clazz.
*/
String me = clazz.getName().replace(".", "/") + ".class";
dirURL = clazz.getClassLoader().getResource(me);
System.out.println(dirURL.toURI());
System.out.println(me);
}
if (dirURL.getProtocol().equals("jar")) {
/* A JAR path */
String jarPath = dirURL.getPath().substring(5, dirURL.getPath().indexOf("!")); //strip out only the JAR file
System.out.println(jarPath);
JarFile jar = new JarFile(URLDecoder.decode(jarPath, "UTF-8"));
Enumeration<JarEntry> entries = jar.entries(); //gives ALL entries in jar
Set<String> result = new HashSet<String>(); //avoid duplicates in case it is a subdirectory
String name = null;
while (entries.hasMoreElements()) {
name = entries.nextElement().getName();
if (name.startsWith(path)) { //filter according to the path
String entry = name.substring(path.length());
int checkSubdir = entry.indexOf("/");
if (checkSubdir >= 0) {
// if it is a subdirectory, we just return the directory name
entry = entry.substring(0, checkSubdir);
}
result.add(entry);
}
}
return result.toArray(new String[result.size()]);
I usually make it a rule to add a resource directory into each JAR with resources that are unique to that JAR held under it. For example (in the Maven structure)
module1/src/main/resources/module1/example.xsd
module2/src/main/resources/module2/example.xsd
The XSDs are then referenced using
InputStream module1XSD= SomeClass.class.getResourceAsStream("/module1/example.xsd");
InputStream module2XSD= SomeClass.class.getResourceAsStream("/module2/example.xsd");
so long as the JARs for module1 and module2 have been placed on the classpath of the application containing SomeClass.
Spring contexts would reference these as
classpath:module1/example.xsd,
classpath:module2/example.xsd
This does mean that you'll have to be able to move the location of XSDs in the JARs that you generate. Maybe even regenerating them through a build script.
If the XSDs all have the same name, you will need another criteria with knowledge of what the "right one" is. Can you embed some information in it (or with it, like a properties file or something) indicating its purpose?
And, a more convenient iterator to get them might be:
getClass().getClassLoader().getResources("/file.xsd");
or something. (Relies on the classpath, whether it is on the file system or in a jar)
When I pass File file to a method I'm trying to get its full path like file.getAbsolutePath(); I always get the same result no matter which one I use either absolute or canonical path PATH_TO_MY_WORKSPACE/projectName/filename and it is not there, how can I get exact location of the file?
Thank you
DETAILS:
Here is some code and this solutions(its bad but its working):
private static void doSomethingToDirectory(File factDir) throws IOException {
File[] dirContents = factDir.listFiles();
if(factDir.isDirectory() && dirContents.length > 0){
for (int i = 0; i < dirContents.length; i++) {
for (String str : dirContents[i].list()) {
if(str.equals(TEMP_COMPARE_FILE)){
process(new File(dirContents[i].getAbsolutePath() + "\\" + str));
}
}
}
}
}
I'm looping trough directories where factDir is src/main, I'm seeking toBeProcessed.txt files only that is TEMP_COMPARE_FILE value and I'm sending them to process method which reads the file and does processing of it.
If someone could better solution I'd be greatful
This quote from the Javadoc might be helpful:
A pathname, whether abstract or in string form, may be either absolute or relative. An absolute pathname is complete in that no other information is required in order to locate the file that it denotes. A relative pathname, in contrast, must be interpreted in terms of information taken from some other pathname. By default the classes in the java.io package always resolve relative pathnames against the current user directory. This directory is named by the system property user.dir, and is typically the directory in which the Java virtual machine was invoked.
I interpret this so that if you create your File object with new File("filename") where filename is a relative path, that path will not be converted into an absolute path even by a call to file.getAbsolutePath().
Update: now that you posted code, I can think of some ways to improve it:
you could use a FilenameFilter to find the desired files,
note that list and listFiles return null for non-directory objects, so we need an extra check for that,
you could also use listFiles() again in the inner loop, thus avoiding the need to create new File objects with hand-assembled paths. (Btw note that appending \\ manually to the path is not portable; the proper way would be to use File.separator).
The end result is
private static void doSomethingToDirectory(File factDir) throws IOException {
if (factDir.isDirectory()) {
for (File file : factDir.listFiles()) {
if (file.isDirectory()) {
for (File child : file.listFiles(new MyFilter())) {
process(child);
}
}
}
}
}
class MyFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return name.equals(TEMP_COMPARE_FILE);
}
}
Note that this code mimics the behaviour of your original piece of code as much as I understood it; most notably, it finds the files with the proper name only in the direct subdirectories of factDir, nonrecursively.
I think there is a way it may help you if and only if the file is in the program directory.
first you get the program directory by :
new File(".").getCanonicalPath()
then :
if file is inside a specific directory like folder\\filename
the full path will be
(new File(".").getCanonicalPath() + "\\folder\\filename")
or if file is directly inside the program directory:
the full path will be
(new File(".").getCanonicalPath() + "\\filename")
i wish this answer help you :)