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

Posted

技术标签:

【中文标题】如何在 Java JAR 文件中获取资源的路径【英文标题】:How to get a path to a resource in a Java JAR file 【发布时间】:2010-10-30 19:35:53 【问题描述】:

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

这有效(在 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)

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

【问题讨论】:

是的。我有一个我想同时使用的类,一个位于外部的文件夹(以防我想更改配置文件的某些参数)和一个将实现配置文件隐藏给用户的 JAR(如可分发的JAR 给所有人)。 所以这个类只接收到一个文件(配置文件)的路径。 那么您可能应该让该类处理输入流,您可以从任一来源获得。 是的,我知道。但如果换一种方式,它会更加清晰和干净。但无论如何,谢谢。 您是否尝试在此问题中执行类似于选项 #4 的操作? ***.com/questions/775389/… 【参考方案1】:

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

如果 java.io.File 是绝对必要的,您可以通过将流复制到一个临时文件并执行相同操作来完成任何通过获取 java.io.File 来完成的操作。

【讨论】:

您可以在调用资源时添加“rsrc:”以打开它。像 new File("rsrc:filename.txt") 这将加载打包在 jar 根目录中的 filename.txt【参考方案2】:

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

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

【讨论】:

+1 这对我有用。确保将要读取的文件放在bin 文件夹中,并在使用`/com/myorg/filename.ext' 路径之前转到资源中加载类的目录。 +1 这也适用于我...我明白这种方法可能存在一些安全风险,因此应用程序所有者需要注意这一点。 您能否澄清一下“使用 getResourceAsStream() 加载资源总是更好的说法”?这如何为问题提供解决方案? @LucaS。这仅适用于图像的情况,抱歉不是很清楚。该方法应该是独立于平台的,尽管它有点老套。 @LucasA 你能澄清一下你提到的风险吗?【参考方案3】:

单行答案是-

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

基本上getResource 方法给出了URL。从此 URL 中,您可以通过调用 toExternalForm() 来提取路径。

【讨论】:

从我的环境 (IntelliJ) 运行时,这会生成一个适用于所有情况的简单文件 URL。但是,当从 jar 本身运行时,我得到一个类似于 jar:file:/path/to/jar/jarname.jar!/file_in_jar.mp4 的 URI。并非所有东西都可以使用以 jar 开头的 URI。以 JavaFX 媒体为例。 我最喜欢这个答案。诚然,在大多数情况下,最好在 jar 文件中简单地将 InputStream 抓取到资源中,但如果由于某种原因您确实需要路径,这就是有效的方法。我需要提供给第三方对象的路径。所以,谢谢! 上述解决方案在 IDE 中不起作用,在 Intellijit 中将 file:/ 添加到 path 中,它在 jar 中有效,但在 IDE 中无效 您的回复解决了我至少 2 天以来一直在努力解决的问题。 TYVM!!【参考方案4】:

我花了一段时间来解决这个问题,因为我发现没有真正有效的解决方案,很奇怪!工作目录通常不是 JAR 的目录,特别是如果 JAR(或任何程序,就此而言)是从 Windows 下的开始菜单运行的。所以这就是我所做的,它适用于从 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.

【讨论】:

【参考方案5】:

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

【讨论】:

当我尝试将这种格式的 URL (...bot.jar!/config/...) 转换为 URI 时,它说路径不是分层的。【参考方案6】:

这是来自用户 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());

         

【讨论】:

【参考方案7】:

您需要了解 jar 文件中的路径。 简单地引用它相对。因此,如果您有一个文件 (myfile.txt),位于 \src\main\resources 目录下的 foo.jar 中(maven 样式)。你可以这样引用它:

src/main/resources/myfile.txt

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

【讨论】:

您确实必须使用正斜杠,即使在 Windows 上也是如此。这意味着您不能使用File.separator【参考方案8】:

在我的例子中,我使用了 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 访问内容。

【讨论】:

【参考方案9】:

文件是文件系统中文件的抽象,文件系统不知道 JAR 的内容是什么。

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

【讨论】:

【参考方案10】:

跟随代码!

/src/main/resources/文件

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;
    

【讨论】:

这是我能够开始工作的唯一解决方案,我为此工作了几个小时。谢谢!【参考方案11】:

以下路径对我有用:classpath:/path/to/resource/in/jar

【讨论】:

@Anatoly 你能在上面分享更多数据吗【参考方案12】:
private static final String FILE_LOCATION = "com/input/file/somefile.txt";

//Method Body


InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

getSystemResourceAsStream 获取此信息是最佳选择。通过获取输入流而不是文件或 URL,可以在 JAR 文件中独立运行。

【讨论】:

【参考方案13】:

可能有点晚了,但您可以使用我的库KResourceLoader 从您的 jar 中获取资源:

File resource = getResource("file.txt")

【讨论】:

【参考方案14】:

在 jar 的 ressources 文件夹 (java/main/resources) 中添加文件(我们假设您添加了一个名为 imports.xml 的 xml 文件),之后你注入ResourceLoader,如果你像下面这样使用弹簧

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

【讨论】:

【参考方案15】:

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

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 我编辑了答案并编写了我在软件中使用的整个方法【参考方案16】:

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

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

【讨论】:

【参考方案17】:

这个Class函数可以通过.jar文件中的相对文件路径获取绝对文件路径。


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


如果目标文件与Utility.java在同一目录下,您的输入将是./file.txt

当您在 getAbsolutePath() 上仅输入 / 时,它会返回 /Users/user/PROJECT_NAME/target/classes/。这意味着您可以像/com/example/file.txt 这样选择文件。

【讨论】:

以上是关于如何在 Java JAR 文件中获取资源的路径的主要内容,如果未能解决你的问题,请参考以下文章

JAVA如何在main()中取得当前jar的名字和路径?

java程序读取资源文件时路径如何指定

如何从Java jar文件中读取资源文件?

Java或者JAR包获取读取资源文件的路径的问题总结

如何把java 文件打成jar

jar方式运行项目-读取jar包中的文件