如何在Java JAR文件中获取资源的路径


166

我正在尝试获取资源的途径,但是我没有运气。

这是可行的(在IDE和JAR中都可以),但是通过这种方式,我无法获取文件的路径,只能获取文件内容:

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

如果我这样做:

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

结果是: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)

有没有办法获取资源文件的路径?


1
是。我有一个要同时使用的类,一个在外部的文件夹(以防我想更改配置文件的某些参数)和一个将实现配置文件隐藏给用户的JAR(例如可分发的) JAR所有人)。
no_ripcord

1
因此,该类仅接收到文件(配置文件)的PATH。
no_ripcord

3
然后,您可能应该让该类处理输入流,您可以从任一来源获取该输入流。
Carl Manaster 09年

1
是的我知道。但这反而会更加清晰和干净。但是无论如何。
no_ripcord 2009年

1
您是否正在尝试执行此问题中的选项4之类的操作?stackoverflow.com/questions/775389/...
埃里克森

Answers:


71

这是故意的。“文件”的内容可能无法作为文件使用。请记住,您正在处理的类和资源可能是JAR文件或其他类型的资源的一部分。类加载器不必为资源提供文件句柄,例如jar文件可能尚未扩展为文件系统中的单个文件。

如果绝对需要java.io.File,则可以通过将流复制到临时文件中并执行相同操作来获得java.io.File。


6
您可以在调用资源时添加“ rsrc:”以打开它。像新的File(“ rsrc:filename.txt”)一样,它将加载filename.txt,它将打包在jar的根目录中
gipsh

63

加载资源时,请确保您注意到以下两者之间的区别:

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

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

我猜想,这种混乱会在加载资源时引起大多数问题。


另外,在加载图像时,它更易于使用getResourceAsStream()

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

当确实需要从JAR归档文件加载(非图像)文件时,可以尝试以下操作:

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!");
}

3
+1这对我有用。bin在使用`/com/myorg/filename.ext'路径之前,请确保将要读取的文件放在文件夹中,并转到资源中加载类的目录。
rayryeng 2014年

+1这也对我有用...我确实知道与该方法有关的安全隐患,因此应用程序所有者需要意识到这一点。
LucasA 2014年

您能澄清一下以下语句“使用getResourceAsStream()加载资源总是更好的选择”吗?这如何为问题提供解决方案?
卡S.15年

@LucaS。那只是针对图像的情况,抱歉,不是很清楚。该方法应该是独立于平台的,尽管这有点古怪。
Tombart

@LucasA您能否澄清您提到的风险?
ZX9

25

单行答案是-

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

基本上,getResource方法给出URL。您可以通过以下网址从该网址提取路径:toExternalForm()

参考文献:

getResource()toExternalForm()


7
从我的环境(IntelliJ)运行时,这会生成一个简单的文件URL,该URL在所有情况下均适用。但是,从jar本身运行时,我得到的URI与jar:file:/path/to/jar/jarname.jar!/file_in_jar.mp4类似。并非所有内容都可以使用以jar开头的URI。JavaFX Media示例。
Noah Ternullo 2014年

1
我最喜欢这个答案。当然,在大多数情况下,将InputStream放在jar文件中时,最好将其抢占到资源,但是如果出于某些原因您确实需要路径,则可以使用该方法。我需要提供给第三方对象的路径。那谢谢啦!
Mario

1
上面的解决方案在IDE中不起作用,在Intellijit中添加file:/了path,它在jar 中可用,但在IDE中
不起作用

您的回复解决了我一直在尝试解决至少2天的问题。TYVM !!
乔纳森

12

我花了一段时间来解决这个问题,因为我发现没有任何解决方案真正有效,奇怪的是!工作目录通常不是JAR的目录,尤其是如果从Windows下的“开始”菜单运行JAR(或任何程序)的话。所以这就是我所做的,它适用于从JAR外部运行的.class文件,也适用于JAR。(我仅在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.
}

8

如果netclient.p位于JAR文件中,则它将没有路径,因为该文件位于其他文件中。在这种情况下,您可以拥有的最佳途径就是file:/path/to/jarfile/bot.jar!/config/netclient.p


当我尝试将这种格式的URL(... bot.jar!/ config / ...)转换为URI时,它表示该路径不是分层的。
gEdringer

7

您需要了解jar文件中的路径。
只是相对地指代它。因此,如果您有一个文件(myfile.txt),位于\src\main\resources目录(Maven风格)下的foo.jar中。您可以这样称呼它:

src/main/resources/myfile.txt

如果使用转储jar,jar -tvf myjar.jar 您将在jar文件中看到输出和相对路径,并使用FORWARD SLASHES。


即使在Windows上,您实际上也必须使用正斜杠。这意味着您不能使用File.separator
STR

3

就我而言,我使用了URL对象而不是Path。

文件

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

使用类加载器的类路径中的资源

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

当我需要阅读内容时,可以使用以下代码:

InputStream stream = url.openStream();

您可以使用InputStream访问内容。


3

这是来自用户Tombart的相同代码,具有流刷新和关闭功能,以避免从jar资源复制不完整的临时文件内容,并具有唯一的临时文件名。

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());
}
         

2

文件是文件系统中文件的抽象,文件系统对JAR的内容一无所知。

尝试使用URI,我认为有一个jar://协议可能对您的目的有用。



1
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

从中获取getSystemResourceAsStream是最好的选择。通过获取输入流而不是文件或URL,它可以在JAR文件中工作并且可以独立运行。



0

在jar文件中时,资源绝对位于包层次结构中(而不是文件系统层次结构中)。因此,如果您使用com.example.Sweet类来加载名为“ ./default.conf”的资源,则该资源的名称将指定为“ /com/example/default.conf”。

但是,如果它在罐子里,那么它不是文件...


0

在jar的ressources文件夹(java / main / resources)内,添加文件(假设您已经添加了一个名为imports.xml的xml文件),之后,ResourceLoader如果使用像下面这样的spring ,则进行注入

@Autowired
private ResourceLoader resourceLoader;

内部游览函数编写以下代码以加载文件:

    Resource resource = resourceLoader.getResource("classpath:imports.xml");
    try{
        File file;
        file = resource.getFile();//will load the file
...
    }catch(IOException e){e.printStackTrace();}

0

也许这种方法可以用于快速解决。

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;
    }
}

您是否省略了上下文?这给了我:java.lang.NullPointerException: Attempt to invoke virtual method 'java.security.CodeSource java.security.ProtectionDomain.getCodeSource()' on a null object reference
艾伦·卢斯

我编辑了答案并写下了我在软件中使用的整个方法
gbii

0

遵循代码!

/ 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;
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.