简体   繁体   English

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

[英]How to get a path to a resource in a Java JAR file

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:这有效(在 IDE 和 JAR 中),但这样我无法获得文件的路径,只能获得文件内容:

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)结果是: java.io.FileNotFoundException: file:/path/to/jarfile/bot.jar!/config/netclient.p (No such file or directory)那个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?有没有办法获取资源文件的路径?

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.请记住,您正在处理可能是 JAR 文件或其他类型资源的一部分的类和资源。 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.类加载器不必为资源提供文件句柄,例如 jar 文件可能没有被扩展为文件系统中的单个文件。

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.如果绝对需要 java.io.File,则可以通过将流复制到临时文件中并执行相同操作来通过获取 java.io.File 来完成任何操作。

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() :此外,当您加载图像时,使用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:当您真的必须从 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!");
}

The one line answer is -单行答案是——

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

Basically getResource method gives the URL.基本上getResource方法给出了 URL。 From this URL you can extract the path by calling toExternalForm() .从此 URL 中,您可以通过调用toExternalForm()来提取路径。

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.工作目录通常不是 JAR 的目录,尤其是在 Windows 下从“开始”菜单运行 JAR(或任何程序)时。 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.所以这就是我所做的,它适用于从 JAR 外部运行的 .class 文件,就像它适用于 JAR 一样。 (I only tested it under Windows 7.) (我只在 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.
}

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

if netclient.p is inside a JAR file, it won't have a path because that file is located inside other file.如果netclient.p在 JAR 文件中,它不会有路径,因为该文件位于其他文件中。 in that case, the best path you can have is really file:/path/to/jarfile/bot.jar!/config/netclient.p .在这种情况下,您可以拥有的最佳路径实际上是file:/path/to/jarfile/bot.jar!/config/netclient.p

You need to understand the path within the jar file.您需要了解 jar 文件中的路径。
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).因此,如果您有一个文件 (myfile.txt),位于\\src\\main\\resources目录下的 foo.jar(maven 样式)。 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.如果您使用jar -tvf myjar.jar转储您的 jar,您将看到 jar 文件中的输出和相对路径,并使用 FORWARD SLASHES。

In my case, I have used a URL object instead Path.就我而言,我使用了 URL 对象而不是 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.您可以使用 InputStream 访问内容。

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.文件是文件系统中文件的抽象,文件系统对 JAR 的内容一无所知。

Try with an URI, I think there's a jar:// protocol that might be useful for your purpouses.尝试使用 URI,我认为有一个 jar:// 协议可能对您的目的有用。

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

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.getSystemResourceAsStream获取它是最好的选择。 By getting the inputstream rather than the file or the URL, works in a JAR file and as stand alone.通过获取输入流而不是文件或 URL,可以在 JAR 文件中独立工作。

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在你的 jar 的 ressources 文件夹(java/main/resources)中添加你的文件(我们假设你已经添加了一个名为import.xml的 xml 文件),然后你注入ResourceLoader如果你像下面这样使用 spring

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

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

File resource = getResource("file.txt")

When in a jar file, the resource is located absolutely in the package hierarchy (not file system hierarchy).jar文件中时,资源绝对位于包层次结构(而不​​是文件系统层次结构)中。 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 .因此,如果您让com.example.Sweet类加载名为./default.conf的资源,则该资源的名称指定为/com/example/default.conf

But if it's in a jar then it's not a File ...但是如果它在一个罐子里,那么它就不是一个文件......

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

follow code!跟随代码!

/src/main/resources/file /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;
    }
}

This Class function can get absolute file path by relative file path in .jar file.这个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();
    }
}

If target file is same directory with Utility.java , your input will be ./file.txt .如果目标文件与Utility.java位于同一目录,则您的输入将为./file.txt

When you input only / on getAbsolutePath(), it returns /Users/user/PROJECT_NAME/target/classes/ .当您仅在 getAbsolutePath() 上输入/时,它会返回/Users/user/PROJECT_NAME/target/classes/ This means you can select the file like this /com/example/file.txt .这意味着您可以像这样选择文件/com/example/file.txt

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM