Java Can';无法在所有计算机上访问JAR中的资源

Java Can';无法在所有计算机上访问JAR中的资源,java,file-io,resources,jar,Java,File Io,Resources,Jar,我正在编写一个应用程序(特别是Bukkit Minecraft服务器的插件)。这样做需要从应用程序的JAR访问.properties文件。这就是我遇到一个奇怪问题的地方。当我在我的开发PC上测试程序时,它运行得很好。.properties文件被加载,一切正常。但是,在我测试它的另一台计算机上,我尝试启动应用程序,但它无法加载属性,InputStream为null。以下是我加载文件的方法: public class Points { private HashMap<String, M

我正在编写一个应用程序(特别是Bukkit Minecraft服务器的插件)。这样做需要从应用程序的JAR访问.properties文件。这就是我遇到一个奇怪问题的地方。当我在我的开发PC上测试程序时,它运行得很好。.properties文件被加载,一切正常。但是,在我测试它的另一台计算机上,我尝试启动应用程序,但它无法加载属性,
InputStream
null
。以下是我加载文件的方法:

public class Points {
    private HashMap<String, MessageFormat> messages;

    public Points() {
         buildMessages();
    }

public static void buildMessages() {
        Properties messageProps = new Properties();
        InputStream in = Points.class.getResourceAsStream("resources/messages.properties");
        messages = new HashMap<String, MessageFormat>();
        Enumeration en;
        try {
            messageProps.load(in);
        } catch(IOException ex) {
            System.err.println("Couldn't read message properties file!");
            return;
        } catch(NullPointerException ex) {
            System.err.println("Couldn't read message properties file!");
            if(in == null)
                System.out.println("IOStream null");
            return;
        }
        en = messageProps.propertyNames();
        while(en.hasMoreElements()) {
            String key = (String)en.nextElement();
            String prop = messageProps.getProperty(key);
            MessageFormat form = new MessageFormat(prop.replaceAll("&", 
                "\u00a7").replaceAll("`", ""));
            messages.put(key, form);
        }
    }
}
所有的
org.bukkit
org.craftbukkit
都是服务器。.properties文件在
buildMessages
方法中加载,该方法由
Points
oneable
方法调用

更新3:在新安装的Arch Linux上,消息属性文件加载正确,一切正常。远程服务器是Ubuntu Linux,我的开发PC是Arch


更新4:好的,这是一种解决方案。这似乎是一个局部问题。我这么说是因为我已经成功地访问了另外两台计算机,并且程序在这两台计算机上都能正确运行。虽然这很烦人,但我的代码或构建脚本似乎没有任何问题。我仍然想知道出了什么问题,但它不再紧迫了。我会继续调查的。谢谢大家。

似乎是不同Java类加载器及其搜索路径之间的细微差别。 在讨论这些细节之前;为什么不试试这个jar文件中的完整路径呢? (例如,类似这样的情况:

Points.class.getResourceAsStream("com/pvminecraft/points/resources/messages.properties");
)


Point.class.getClassLoader().getResourceAsStream(“com/pvminecraft/points/resources/messages.properties”)

在没有第一个“/”的情况下尝试它,它应该可以在任何运行JVM的地方工作

如果不起作用,请尝试将该文件放在JAR文件的根目录中,然后重试

如果仍然不起作用,请尝试使用以下方法:

public static byte[] getFile(File zip, String fileName) throws FileNotFoundException, ZipException, IOException {
        String filename = fileName;

        if (!zip.exists()) {
            throw new FileNotFoundException(zip.getName());
        }
        while (filename.charAt(0) == '/' || filename.charAt(0) == '\\') {
            filename = filename.substring(1);
        }

        if (filename.contains("\\")) {
            filename = filename.replace("\\", "/");
        }

        ZipFile zipFile = new ZipFile(zip);
        Enumeration entries = zipFile.entries();

        ByteArrayOutputStream output;
        byte[] result = null;

        while (entries.hasMoreElements()) {
            ZipEntry entry = (ZipEntry) entries.nextElement();

            if (entry.getName().equalsIgnoreCase(filename)) {
                FileUtils.copyInputStream(zipFile.getInputStream(entry), output = new ByteArrayOutputStream());
                result = output.toByteArray();
                zipFile.close();
                output.close();
                return result;
            }
        }

        zipFile.close();
        throw new FileNotFoundException(filename);
    }
你需要这个

public static void copyInputStream(InputStream in, OutputStream out) throws IOException {
    byte[] buffer = new byte[1024];
    int len;
    while (((len = in.read(buffer)) >= 0)) {
        out.write(buffer, 0, len);
    }
    out.flush();
}
获取运行jar的路径

 String currentJar = "";
                                        // Get current jar path. Since user may rename this file, we need to do this way
              try {
                   currentJar = (Points.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
                   if (currentJar.startsWith("/")) currentJar = currentJar.substring(1);
                   } catch (URISyntaxException ex) {
                   }
第一个“/”我真的不记得它出现的原因,但它确实出现了,因此您必须删除它:

最后调用方法:
getFile(currentJar,“路径到属性文件”)

您将使用一个字节数组。只要把它作为一个ByteArrayInputStream,你的问题就会得到解决


这段代码是我创建的util类的一部分,这就是为什么不需要读取字节数组的原因,但对于ofc,您可以将其更改为直接使用Properties.load()方法的InputStream

ZIP util类的链接

FileUtils util类的链接


您可能还需要确保构建脚本(Ant、Maven)或IDE是否没有从生成的JAR文件中删除/重新定位messages.properties(因为它不是.class)。您可以使用7zip或WinZip之类的工具检查JAR内容。

因为您正在加载与类相同的包(
)中的资源,所以不需要使用绝对路径来加载它

服务器是否使用任何类型的缓存来加载插件?这可能是由于类路径中存在较旧版本的插件jar造成的。为了验证服务器是否真的加载了正确版本的jar文件,您可以尝试部署一个插件版本,该插件将一些内容记录到控制台,并查看是否发生了一些事情(如果消息确实被记录)

另外,我不知道类加载器层次结构在服务器中是如何组织的,但是您可以尝试从当前线程类加载器(通常是根父类加载器,它将在每个其他子类加载器中查找资源)加载资源。你必须使用绝对路径

ClassLoader rootCL = Thread.currentThread().getContextClassLoader();
InputStream resource = rootCL.getResourceAsStream(
        "/com/pvminecraft/points/resources/messages.properties");

检查此项以了解有关不同类加载器的更多信息。

目录“资源”不在类路径上-请参阅Tom的答案。然而,“com/pvminecraft/points/resources”目录是。检查您的开发环境-我相信您已经在类路径上的某个位置设置了类路径或属性文件的副本。请发布整个堆栈跟踪。这里的行号是什么:
位于com.pvminecraft.points.points.buildMessages(未知源)
。您的JDK版本是什么?@Kowser我使用JDK 6u26是因为我与JDK 7存在向后兼容性问题。您可以将您的属性放入清单文件中吗?您可以从清单文件读取属性吗?这与更改的情况相同。在我的电脑上工作,但在另一台电脑上不工作。@MichaelSmith-你是如何在你的电脑上运行它的?@Paul正如我前面提到的,它们是服务器应用程序的插件。服务器启动并加载
plugins
目录中的所有插件。我在两台电脑上运行的是同一版本的服务器。第二台电脑实际上是一台主机,我无法免费访问。就服务器而言,两者之间的唯一区别在于它们位于不同的机器上。我想主机可能会以不同的方式配置Java?@MichaelSmith,将属性文件移动到类路径的根目录,即与“com”位于同一目录中,并将其引用为
/messages.properties
。这不是Java配置问题,尽管远程服务器可能也有一个文件
messages.properties
,您不允许加载该文件,因此,您也可以尝试将
messages.properties
重命名为
smith messages.properties
…一些独特的东西。@Paul我将其重命名为
points messages.properties
,并将其移动到jar的根目录下。同样的问题仍然存在。
 String currentJar = "";
                                        // Get current jar path. Since user may rename this file, we need to do this way
              try {
                   currentJar = (Points.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());
                   if (currentJar.startsWith("/")) currentJar = currentJar.substring(1);
                   } catch (URISyntaxException ex) {
                   }
ClassLoader rootCL = Thread.currentThread().getContextClassLoader();
InputStream resource = rootCL.getResourceAsStream(
        "/com/pvminecraft/points/resources/messages.properties");