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

我试图得到一个资源的途径,但我没有运气。

这种方法(在 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)

是否有获取资源文件路径的方法?

347933 次浏览

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

只要能拿到 java.io 就行。文件可以通过将流复制到一个临时文件中来完成,如果 java.io。文件是绝对必要的。

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

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

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

您需要理解 jar 文件中的路径。
只是相对而言。因此,如果您有一个文件(myfile.txt) ,位于 \src\main\resources目录下的 foo.jar 中(maven 样式)。你可以这样说:

src/main/resources/myfile.txt

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

我花了一段时间来处理这个问题,因为我找到的解决方案没有一个真正起作用,真是够奇怪的!这个工作目录通常不在 JAR 的目录中,特别是当一个 JAR (或者任何程序)在 Windows 下的开始菜单中运行的时候。所以这就是我所做的。类文件从 JAR 外部运行,就像它在 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.
}

下面的方法对我很有效: classpath:/path/to/resource/in/jar

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

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

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

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

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


//Method Body




InputStream invalidCharacterInputStream = URLClassLoader.getSystemResourceAsStream(FILE_LOCATION);

getSystemResourceAsStream获得这个是最好的选择。通过获取输入流而不是文件或 URL,可以在 JAR 文件中独立工作。

唯一的答案是

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

基本上 getResource方法给出 URL。从这个 URL 你可以通过调用 toExternalForm()提取路径。

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

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

在 jar 的 resresources 文件夹(java/main/resources)中添加文件(我们假设您已经添加了一个名为 Xml 的 xml 文件) ,然后如果您使用类似于下面的 spring,则注入 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();}

这可能有点晚,但是你可以使用我的库 KResourceLoader从你的 jar 中获得一个资源:

File resource = getResource("file.txt")

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

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

遵守指令!

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

这个 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文件。