在 Java 中,为什么 class.getResource("/path/to/res");将文件复制到系统时无法在可运行的 jar 中工作?

In Java, why does class.getResource("/path/to/res"); not work in the runnable jar when copying files to the system?

我一直在做一个项目,要求用户第一次 "install" 程序 运行 它。此安装需要将我的"res"文件夹中的所有资源复制到用户硬盘上的专用目录中。我有以下代码块工作得很好,但是当我从 eclipse 导出可运行的 jar 时,我收到了一个堆栈跟踪,表明 InputStream 为空。安装循环将数组列表中每个文件的路径传递给导出函数,这就是问题所在(使用 InputStream)。路径在 Eclipse 和可运行的 jar 中都被正确传递,所以我怀疑这是问题所在。我已经完成我的研究并发现了其他类似的问题,但是 none 的建议修复(使用类加载器等)已经奏效。我不明白为什么我现在的方法在 Eclipse 中有效,但在 jar 中却无效?

(还有一个名为 installFiles 的文件 ArrayList)

private static String installFilesLocationOnDisk=System.getProperty("user.home")+"/Documents/[project name]/Resources/";

public static boolean tryInstall(){
    for(File file:installFiles){
//for each file, make the required directories for its extraction location
        new File(file.getParent()).mkdirs();
        try {
//export the file from the jar to the system 
            exportResource("/"+file.getPath().substring(installFilesLocationOnDisk.length()));
        } catch (Exception e) {
            return false;
        }
    }
    return true;
}

private static void exportResource(String resourceName) throws Exception {
    InputStream resourcesInputStream = null;
    OutputStream resourcesOutputStream = null;
    //the output location for exported files
    String outputLocation = new File(installFilesLocationOnDisk).getPath().replace('\', '/');
    try {
        //This is where the issue arises when the jar is exported and ran.
        resourcesInputStream = InstallFiles.class.getResourceAsStream(resourceName);
        if(resourcesInputStream == null){
                throw new Exception("Cannot get resource \"" + resourceName + "\" from Jar file.");
        }
        //Write the data from jar's resource to system file
        int readBytes;
        byte[] buffer = new byte[4096];
        resourcesOutputStream = new FileOutputStream(outputLocation + resourceName);
        while ((readBytes = resourcesInputStream.read(buffer)) > 0) {
            resourcesOutputStream.write(buffer, 0, readBytes);
        }
    } catch (Exception ex) {
        ex.printStackTrace();
        System.exit(1);
    } finally {
        //Close streams
        resourcesInputStream.close();
        resourcesOutputStream.close();
    }
}

堆栈跟踪:

java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.

感谢所有帮助!谢谢

将您的 JAR 文件重命名为 ZIP,解压缩并检查资源去了哪里。

您有可能将 Maven 与 Eclipse 一起使用,这意味着使用 Eclipse 的功能导出 Runnable JAR 不会将资源正确地放置在 JAR 中(如果您是,它们将最终位于 JAR 中的文件夹资源下)使用默认的 Maven 文件夹名称约定)。

如果是这种情况,您应该使用 Maven 的 Assembly 插件(或 "uber-JAR" 的 Shade 插件)来创建可运行的 JAR。

即使您没有使用 Maven,您也应该检查资源是否正确放置在生成的 JAR 中。

P.S。也不要这样做:

.getPath().replace('\', '/');

永远不要依赖特定的分隔符 - 使用 java.io.File.separator 来确定系统的文件分隔符。

Stack Trace:

java.lang.Exception: Cannot get resource "/textures\gameIcon.png" from Jar file.

资源错误时的名称。正如 ClassLoader.getResource(String) 的 Javadoc 所描述的(Class.getResourceAsStream(String) 参考 ClassLoader 的详细信息):

The name of a resource is a /-separated path name that identifies the resource.

无论您是从文件系统还是从 Jar 文件获取资源,都应该始终使用 / 作为分隔符。

使用 \ 有时可能有效,有时无效:无法保证。但是总是报错。

对于您的情况,解决方案是改变您调用 exportResource:

的方式
String path = file.getPath().substring(installFilesLocationOnDisk.length());
exportResource("/" + path.replace(File.pathSeparatorChar, '/'));