186

I am trying to get a path to a Resource but I have had no luck.

This works (both in IDE and with the JAR) but this way I can't get a path to a file, only the file contents:

ClassLoader classLoader = getClass().getClassLoader();
PrintInputStream(classLoader.getResourceAsStream("config/netclient.p"));

If I do this:

ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("config/netclient.p").getFile());

The result is: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

Is there a way to get a path to a resource file?

noripcord
  • 3,412
  • 5
  • 29
  • 26
  • May I ask why you would need the path? – Michael Myers Jun 02 '09 at 20:36
  • 1
    Yes. I've got a class that I would like to work with both, a folder on the outside (in case I want to change some parameter of the config file) and a JAR that hides the implementation configuration files to the user (like a distributable JAR to all people). – noripcord Jun 02 '09 at 20:41
  • 1
    So the class just receives a PATH to a file (the config file). – noripcord Jun 02 '09 at 20:42
  • 3
    Then you should probably have that class deal with an input stream, which you can get from either source. – Carl Manaster Jun 02 '09 at 20:43
  • 1
    Yes, I know. But it would have been more clear and clean the other way. But thx anyway. – noripcord Jun 02 '09 at 20:47
  • 1
    Are you trying to do something like option #4 in this question? http://stackoverflow.com/questions/775389/accessing-properties-files-outside-the-jar/775565 – erickson Jun 02 '09 at 22:36
  • 1
    well, i have pretty much the same problem. i want to be able to bundle and image in a JAR and then at runtime use that image in a JTextPanel using HTML. HTML requires either a relative path (and set the document base to something appropriate) or an absolute path. Can you get that path for a file in a JAR? – twolfe18 Jul 16 '09 at 17:08
  • 1
    No, you cant. You can get an InputStream of the file but you can't get a path. – noripcord Jul 20 '09 at 23:24

17 Answers17

79

This is deliberate. The contents of the "file" may not be available as a file. Remember you are dealing with classes and resources that may be part of a JAR file or other kind of resource. The classloader does not have to provide a file handle to the resource, for example the jar file may not have been expanded into individual files in the file system.

Anything you can do by getting a java.io.File could be done by copying the stream out into a temporary file and doing the same, if a java.io.File is absolutely necessary.

Neal Maloney
  • 1,174
  • 8
  • 5
  • 6
    you can add 'rsrc:' when you call your resource in order to open it. like new File("rsrc:filename.txt") this will load filename.txt which is packed inside the root of your jar – gipsh Jun 16 '16 at 18:45
76

When loading a resource make sure you notice the difference between:

getClass().getClassLoader().getResource("com/myorg/foo.jpg") //relative path

and

getClass().getResource("/com/myorg/foo.jpg")); //note the slash at the beginning

I guess, this confusion is causing most of problems when loading a resource.


Also, when you're loading an image it's easier to use getResourceAsStream():

BufferedImage image = ImageIO.read(getClass().getResourceAsStream("/com/myorg/foo.jpg"));

When you really have to load a (non-image) file from a JAR archive, you might try this:

File file = null;
String resource = "/com/myorg/foo.xml";
URL res = getClass().getResource(resource);
if (res.getProtocol().equals("jar")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile("tempfile", ".tmp");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        Exceptions.printStackTrace(ex);
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}

if (file != null && !file.exists()) {
    throw new RuntimeException("Error: File " + file + " not found!");
}
spottedmahn
  • 14,823
  • 13
  • 108
  • 178
Tombart
  • 30,520
  • 16
  • 123
  • 136
  • 3
    +1 This worked for me. Make sure you place the files that you want to read in in the `bin` folder and go to the directory of the class loading in the resources before using the `/com/myorg/filename.ext' path. – rayryeng Apr 25 '14 at 16:58
  • +1 This also works for me... I do understand that there might be some security risks related to this approach, so application owners need to be aware of that. – LucasA Oct 27 '14 at 13:39
  • could you clarify this statement "It's always better to load a resource with getResourceAsStream()" ? How can this provide a solution to the problem? – Luca S. Nov 02 '15 at 16:26
  • @LucaS. That was meant just for the case of images, sorry it wasn't very clear. The approach should be platform independent, though it's a bit hacky way. – Tombart Nov 03 '15 at 16:11
  • @LucasA Could you clarify the risks you mentioned? – ZX9 Jan 26 '16 at 18:24
  • i'm reading a wav file from my jar file and wrote it like in your example to my tmp dir. but the files are different. what happened and what can i do to prevent this? – tibi Aug 17 '16 at 11:52
  • never mind, in my pom i forgot to exclude wav files from filtering :'( – tibi Aug 17 '16 at 12:00
  • If you want to get a file in e.g. the root of the jar, an absolute path should be specified. Also, *any* class will do; example: URL url = Object.class.getResource("/" + filename); – Per Lindberg Oct 26 '16 at 14:20
37

The one line answer is -

String path = this.getClass().getClassLoader().getResource(<resourceFileName>).toExternalForm()

Basically getResource method gives the URL. From this URL you can extract the path by calling toExternalForm().

Maicon Mauricio
  • 2,052
  • 1
  • 13
  • 29
Manojkumar Khotele
  • 963
  • 11
  • 25
  • 10
    While running from my environment (IntelliJ) this produces a simple file URL which works in all cases. However, when running from the jar itself, I get a URI similar to jar:file:/path/to/jar/jarname.jar!/file_in_jar.mp4. Not everything can utilize a URI that begins with jar. Case in point JavaFX Media. – Noah Ternullo Dec 17 '14 at 01:01
  • 2
    I like this answer best. Granted, in most cases it may be preferable to simply grab the InputStream to the resource when it's in a jar file, but if for some reason you really need the path, this is what works. I needed the path to give to a third-party object. So, thanks! – Mario Jun 21 '16 at 15:36
  • 1
    the above solution does not work while in IDE, in Intellijit adds `file:/` to path , which works in jar but not in IDE – Tayab Hussain Jan 13 '19 at 11:25
13

I spent a while messing around with this problem, because no solution I found actually worked, strangely enough! The working directory is frequently not the directory of the JAR, especially if a JAR (or any program, for that matter) is run from the Start Menu under Windows. So here is what I did, and it works for .class files run from outside a JAR just as well as it works for a JAR. (I only tested it under Windows 7.)

try {
    //Attempt to get the path of the actual JAR file, because the working directory is frequently not where the file is.
    //Example: file:/D:/all/Java/TitanWaterworks/TitanWaterworks-en.jar!/TitanWaterworks.class
    //Another example: /D:/all/Java/TitanWaterworks/TitanWaterworks.class
    PROGRAM_DIRECTORY = getClass().getClassLoader().getResource("TitanWaterworks.class").getPath(); // Gets the path of the class or jar.

    //Find the last ! and cut it off at that location. If this isn't being run from a jar, there is no !, so it'll cause an exception, which is fine.
    try {
        PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('!'));
    } catch (Exception e) { }

    //Find the last / and cut it off at that location.
    PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(0, PROGRAM_DIRECTORY.lastIndexOf('/') + 1);
    //If it starts with /, cut it off.
    if (PROGRAM_DIRECTORY.startsWith("/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(1, PROGRAM_DIRECTORY.length());
    //If it starts with file:/, cut that off, too.
    if (PROGRAM_DIRECTORY.startsWith("file:/")) PROGRAM_DIRECTORY = PROGRAM_DIRECTORY.substring(6, PROGRAM_DIRECTORY.length());
} catch (Exception e) {
    PROGRAM_DIRECTORY = ""; //Current working directory instead.
}
DeProgrammer
  • 155
  • 1
  • 2
10

This is same code from user Tombart with stream flush and close to avoid incomplete temporary file content copy from jar resource and to have unique temp file names.

File file = null;
String resource = "/view/Trial_main.html" ;
URL res = getClass().getResource(resource);
if (res.toString().startsWith("jar:")) {
    try {
        InputStream input = getClass().getResourceAsStream(resource);
        file = File.createTempFile(new Date().getTime()+"", ".html");
        OutputStream out = new FileOutputStream(file);
        int read;
        byte[] bytes = new byte[1024];

        while ((read = input.read(bytes)) != -1) {
            out.write(bytes, 0, read);
        }
        out.flush();
        out.close();
        input.close();
        file.deleteOnExit();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
} else {
    //this will probably work in your IDE, but not from a JAR
    file = new File(res.getFile());
}
         
spottedmahn
  • 14,823
  • 13
  • 108
  • 178
Bruce
  • 793
  • 8
  • 17
8

if netclient.p is inside a JAR file, it won't have a path because that file is located inside other file. in that case, the best path you can have is really file:/path/to/jarfile/bot.jar!/config/netclient.p.

cd1
  • 15,908
  • 12
  • 46
  • 47
  • When I try and convert a URL of this format (...bot.jar!/config/...) to URI it says that the path is not hierarchical. – gEdringer Jun 06 '18 at 10:12
7

You need to understand the path within the jar file.
Simply refer to it relative. So if you have a file (myfile.txt), located in foo.jar under the \src\main\resources directory (maven style). You would refer to it like:

src/main/resources/myfile.txt

If you dump your jar using jar -tvf myjar.jar you will see the output and the relative path within the jar file, and use the FORWARD SLASHES.

RevanthKrishnaKumar V.
  • 1,855
  • 1
  • 21
  • 34
eric manley
  • 99
  • 1
  • 2
  • You indeed have to use forward slashes, even on Windows. That implies that you cannot use `File.separator`. – str Apr 04 '16 at 08:31
5

In my case, I have used a URL object instead Path.

File

File file = new File("my_path");
URL url = file.toURI().toURL();

Resource in classpath using classloader

URL url = MyClass.class.getClassLoader().getResource("resource_name")

When I need to read the content, I can use the following code:

InputStream stream = url.openStream();

And you can access the content using an InputStream.

jmgoyesc
  • 2,677
  • 1
  • 18
  • 16
3

follow code!

/src/main/resources/file

streamToFile(getClass().getClassLoader().getResourceAsStream("file"))

public static File streamToFile(InputStream in) {
    if (in == null) {
        return null;
    }

    try {
        File f = File.createTempFile(String.valueOf(in.hashCode()), ".tmp");
        f.deleteOnExit();

        FileOutputStream out = new FileOutputStream(f);
        byte[] buffer = new byte[1024];

        int bytesRead;
        while ((bytesRead = in.read(buffer)) != -1) {
            out.write(buffer, 0, bytesRead);
        }

        return f;
    } catch (IOException e) {
        LOGGER.error(e.getMessage(), e);
        return null;
    }
}
seunggabi
  • 1,699
  • 12
  • 12
  • 1
    This is the only solution I was able to get to work, and I was working on this for hours. Thanks! – Randy Sep 05 '21 at 12:14
2

A File is an abstraction for a file in a filesystem, and the filesystems don't know anything about what are the contents of a JAR.

Try with an URI, I think there's a jar:// protocol that might be useful for your purpouses.

fortran
  • 74,053
  • 25
  • 135
  • 175
1

The following path worked for me: classpath:/path/to/resource/in/jar

Baz
  • 36,440
  • 11
  • 68
  • 94
Anatoly
  • 5,119
  • 1
  • 14
  • 8
1
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

Getting this from getSystemResourceAsStream is the best option. By getting the inputstream rather than the file or the URL, works in a JAR file and as stand alone.

rink.attendant.6
  • 44,500
  • 61
  • 101
  • 156
Shano
  • 330
  • 2
  • 5
1

It may be a little late but you may use my library KResourceLoader to get a resource from your jar:

File resource = getResource("file.txt")
Lamberto Basti
  • 478
  • 1
  • 6
  • 24
0

When in a jar file, the resource is located absolutely in the package hierarchy (not file system hierarchy). So if you have class com.example.Sweet loading a resource named ./default.conf then the resource's name is specified as /com/example/default.conf.

But if it's in a jar then it's not a File ...

Maicon Mauricio
  • 2,052
  • 1
  • 13
  • 29
0

Inside your ressources folder (java/main/resources) of your jar add your file (we assume that you have added an xml file named imports.xml), after that you inject ResourceLoader if you use spring like bellow

@Autowired
private ResourceLoader resourceLoader;

inside tour function write the bellow code in order to load file:

    Resource resource = resourceLoader.getResource("classpath:imports.xml");
    try{
        File file;
        file = resource.getFile();//will load the file
...
    }catch(IOException e){e.printStackTrace();}
BERGUIGA Mohamed Amine
  • 6,094
  • 3
  • 40
  • 38
0

Maybe this method can be used for quick solution.

public class TestUtility
{ 
    public static File getInternalResource(String relativePath)
    {
        File resourceFile = null;
        URL location = TestUtility.class.getProtectionDomain().getCodeSource().getLocation();
        String codeLocation = location.toString();
        try{
            if (codeLocation.endsWith(".jar"){
                //Call from jar
                Path path = Paths.get(location.toURI()).resolve("../classes/" + relativePath).normalize();
                resourceFile = path.toFile();
            }else{
                //Call from IDE
                resourceFile = new File(TestUtility.class.getClassLoader().getResource(relativePath).getPath());
            }
        }catch(URISyntaxException ex){
            ex.printStackTrace();
        }
        return resourceFile;
    }
}
gbii
  • 109
  • 2
  • 5
  • Are you omitting some context? This gives me: `java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference` – Allen Luce Sep 25 '19 at 20:12
  • I edited the answer and wrote the whole method which i used in a software – gbii Sep 27 '19 at 14:13
0

This Class function can get absolute file path by relative file path in .jar file.


public class Utility {

    public static void main(String[] args) throws Exception {
        Utility utility = new Utility();
        String absolutePath = utility.getAbsolutePath("./absolute/path/to/file");
    }

    public String getAbsolutePath(String relativeFilePath) throws IOException {
        URL url = this.getClass().getResource(relativeFilePath);
        return url.getPath();
    }
}

If target file is same directory with Utility.java, your input will be ./file.txt.

When you input only / on getAbsolutePath(), it returns /Users/user/PROJECT_NAME/target/classes/. This means you can select the file like this /com/example/file.txt.

Nori
  • 2,340
  • 1
  • 18
  • 41