如何在Java中从文件资源加载资源包?

53

我有一个名为mybundle.txt的文件在c:/temp中 -

c:/temp/mybundle.txt

如何将此文件加载到java.util.ResourceBundle中?该文件是有效的资源包。

以下方法似乎不起作用:

java.net.URL resourceURL = null;

String path = "c:/temp/mybundle.txt";
java.io.File fl = new java.io.File(path);

try {
   resourceURL = fl.toURI().toURL();
} catch (MalformedURLException e) {             
}           

URLClassLoader urlLoader = new URLClassLoader(new java.net.URL[]{resourceURL});
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle( path , 
                java.util.Locale.getDefault(), urlLoader );
14个回答

79
只要您正确命名资源束文件(使用.properties扩展名),就可以这样做:
File file = new File("C:\\temp");
URL[] urls = {file.toURI().toURL()};
ClassLoader loader = new URLClassLoader(urls);
ResourceBundle rb = ResourceBundle.getBundle("myResource", Locale.getDefault(), loader);

这里的"c:\temp"是一个外部文件夹(不在类路径上),它包含属性文件,而"myResource"与myResource.properties、myResource_fr_FR.properties等相关。

感谢http://www.coderanch.com/t/432762/java/java/absolute-path-bundle-file提供的帮助。


这个答案完全匹配。感谢分享。 - jmcollin92
谢谢,这绝对是处理插件等直接路径到类加载器的方法... - Daniel B. Chapman
但是也许这会每次重新加载包? - Seby
注意:您可能应该关闭 URLClassLoader,否则文件将在您的应用程序运行期间被锁定。 - ST-DDT

51
当您说它是“有效的资源束”时,它是否是属性资源束?如果是这样,最简单的加载方法可能如下所示:
try (FileInputStream fis = new FileInputStream("c:/temp/mybundle.txt")) {
  return new PropertyResourceBundle(fis);
}

2
嘿,Jon,这不是忽略了本地化问题吗?这应该是使用 bundle 的主要原因吧? - Nick Holt
3
没有迹象表明他实际上有多个文件。它带有 .txt 后缀并不是很令人鼓舞。但是,在那种情况下确实会失败。 - Jon Skeet
9
与其手动加载文件(即使用FileInputStream)并构造 PropertyResourceBundle,难道不应该使用 ResourceBundle.getBundle(...) 方法才是“正确”的方法吗? - Vihung
5
ResourceBundle.getBundle() 方法将从类路径加载资源包,并使用给定的 Locale,而不是像提问者期望的那样加载原始文件。请注意,这里只涉及翻译内容本身,不提供其他解释或附加信息。 - Matthieu

22

1) 将扩展名更改为properties(例如mybundle.properties)。
2) 将文件放入jar文件中并将其添加到类路径中。
3) 使用以下代码访问属性:

ResourceBundle rb = ResourceBundle.getBundle("mybundle");
String propertyValue = rb.getString("key");

如果使用Launch4j将jar文件嵌入可执行文件中,那么很遗憾这是不可能的... - Matthieu
3
属性文件不必在JAR包内部。如果类在包a.b.c.MyClass.java中,则mybundle.properties必须在a父级文件夹中,即类路径。 例如,对于类/myproject/a/b/c/MyClass.java,资源包必须放置在/myproject/mybundle.properties中。 - ccpizza
我正在使用Maven。我将文件放在资源文件夹中,例如src/test/resourcessrc/main/resources,它可以正常工作。然后,我发现它被复制到了target/test-classes,这实际上与您的评论相匹配。我认为这是因为文件夹target/test-classes是类路径的一部分。 - tarekahf

11

ResourceBundle.getBundle(String baseName)的JavaDocs中提到:

baseName - 资源包的基本名称,一个完全限定的类名

简单来说,这意味着资源包必须在类路径上,并且baseName应该是包含资源包的包和资源包名称,例如您的情况下为mybundle

不需要添加扩展名和任何作为资源包名称一部分的区域设置,JVM将根据默认区域设置对其进行排序,有关更多信息,请参见java.util.ResourceBundle文档。


7

针对JSF应用程序

从给定的文件路径获取资源包属性文件以在JSF应用程序中使用。

  • 使用URLClassLoader设置继承ResourceBundle的类的bundle,以从文件路径加载bundle。
  • loadBundle标签的basename属性中指定类。 <f:loadBundle basename="Message" var="msg" />

有关扩展RB的基本实现,请参见示例自定义资源包

/* Create this class to make it base class for Loading Bundle for JSF apps */
public class Message extends ResourceBundle {
        public Messages (){
                File file = new File("D:\\properties\\i18n");  
                ClassLoader loader=null;
                   try {
                       URL[] urls = {file.toURI().toURL()};  
                       loader = new URLClassLoader(urls); 
                       ResourceBundle bundle = getBundle("message", FacesContext.getCurrentInstance().getViewRoot().getLocale(), loader);
                       setParent(bundle);
                       } catch (MalformedURLException ex) { }
       }
      .
      .
      .
    }

否则,从getBundle方法获取捆绑包,但是语言环境从其他来源获取,例如Locale.getDefault(),在这种情况下可能不需要新的(RB)类。

5
如果您像我一样,实际上想从文件系统加载 .properties 文件而不是类路径,但仍然保留与查找相关的所有智能功能,则需要执行以下操作:
  1. 创建 java.util.ResourceBundle.Control 的子类
  2. 覆盖 newBundle() 方法
在这个愚蠢的例子中,假设您有一个包含 ".properties" 文件的扁平列表的文件夹位于 C:\temp
public class MyControl extends Control {
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
        throws IllegalAccessException, InstantiationException, IOException {

    if (!format.equals("java.properties")) {
        return null;
    }

    String bundleName = toBundleName(baseName, locale);
    ResourceBundle bundle = null;

    // A simple loading approach which ditches the package      
    // NOTE! This will require all your resource bundles to be uniquely named!
    int lastPeriod = bundleName.lastIndexOf('.');

    if (lastPeriod != -1) {
        bundleName = bundleName.substring(lastPeriod + 1);
    }
    InputStreamReader reader = null;
    FileInputStream fis = null;
    try {

        File file = new File("C:\\temp\\mybundles", bundleName);

        if (file.isFile()) { // Also checks for existance
            fis = new FileInputStream(file);
            reader = new InputStreamReader(fis, Charset.forName("UTF-8"));
            bundle = new PropertyResourceBundle(reader);
        }
    } finally {
        IOUtils.closeQuietly(reader);
        IOUtils.closeQuietly(fis);
    }
    return bundle;
}

另外请注意,这支持UTF-8,否则默认情况下可能不支持。


支持UTF-8编码的.properties文件,其中包含非ASCII ISO-8859-1字符(码点128-255),这是合法的。ISO-8859-1使用一个字节来表示它们,而UTF-8则使用两个字节来表示。 - Christoffer Hammarström
这条评论是不正确的。UTF-8有可变数量的字节,并且是国际化使用ResourceBundles的正确答案 - 这也是使用ResourceBundles的目的。请参阅此相关问题和相关的已接受答案。https://dev59.com/HWkv5IYBdhLWcg3w9lei - Darrell Teague

3

这对我非常有帮助。而且它不会每次重新加载bundle。我试图获取一些统计数据,从外部文件位置加载和重新加载bundle。

File file = new File("C:\\temp");
URL[] urls = {file.toURI().toURL()};
ClassLoader loader = new URLClassLoader(urls);
ResourceBundle rb = ResourceBundle.getBundle("myResource", Locale.getDefault(), loader);

其中"c:\temp"是外部文件夹(不在类路径上),其中保存了属性文件,而"myResource"相关于myResource.properties、myResource_fr_FR.properties等。

注意: 如果在类路径上有相同的包名称,则使用URLClassLoader的此构造函数将自动选择该包。

感谢http://www.coderanch.com/t/432762/java/java/absolute-path-bundle-file

以下是一些统计数据,所有时间都以毫秒为单位。 我不担心初始加载时间,因为那可能是我的工作区或代码问题,我正在尝试解决,但我想要展示的是重新加载所花费的时间更少,这告诉我它来自内存。

以下是一些统计数据:

  • 初始Locale_1加载需要3486
  • 重新加载Locale_1需要24
  • 重新加载Locale_1需要23
  • 重新加载Locale_1需要22
  • 重新加载Locale_1需要15
  • 初始Locale_2加载需要870
  • 重新加载Locale_2需要22
  • 重新加载Locale_2需要18
  • 初始Locale_3加载需要2298
  • 重新加载Locale_3需要8
  • 重新加载Locale_3需要4

2
我更倾向于使用ResourceBundle类来加载属性 - 只需一行代码就可以完成,而不是通过流、Properties类和load()函数编写五行代码。
FYI ....
    public void init(ServletConfig servletConfig) throws ServletException {
    super.init(servletConfig);

    try {

            /*** Type1 */
        Properties props = new Properties();

        String fileName = getServletContext().getRealPath("WEB-INF/classes/com/test/my.properties");
    //          stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(fileName);
    //          stream = ClassLoader.getSystemResourceAsStream("WEB-INF/class/com/test/my.properties");  

        InputStream stream = getServletContext().getResourceAsStream("/WEB-INF/classes/com/test/my.properties");

  //        props.load(new FileInputStream(fileName));
        props.load(stream);

        stream.close();
        Iterator keyIterator = props.keySet().iterator();
        while(keyIterator.hasNext()) {
                String key = (String) keyIterator.next();
                String value = (String) props.getProperty(key);
                System.out.println("key:" + key + " value: " + value);
        }

  /*** Type2:  */
  // Just get it done in one line by rb instead of 5 lines to load the properties
  // WEB-INF/classes/com/test/my.properties file            
  //            ResourceBundle rb = ResourceBundle.getBundle("com.test.my", Locale.ENGLISH, getClass().getClassLoader());
        ResourceBundle rb = ResourceBundle.getBundle("com.ibm.multitool.customerlogs.ui.nl.redirect");
        Enumeration<String> keys = rb.getKeys();
        while(keys.hasMoreElements()) {
            String key = keys.nextElement();
            System.out.println(key + " - " + rb.getObject(key));
        }
    } catch (IOException e) {
        e.printStackTrace();
        throw new ServletException("Error loading config.", e);
    } catch (Exception e) {
        e.printStackTrace();
        throw new ServletException("Error loading config.", e);
    }       

}

0
ResourceBundle rb = ResourceBundle.getBundle("service"); //service.properties
System.out.println(rb.getString("server.dns")); //server.dns=http://....

你能提供更多关于你的回答的信息吗? - JAL

0

我认为你想要文件的父级在类路径上,而不是实际的文件本身。

尝试这个(可能需要一些调整):

String path = "c:/temp/mybundle.txt";
java.io.File fl = new java.io.File(path);

try {
   resourceURL = fl.getParentFile().toURL();
} catch (MalformedURLException e) {
   e.printStackTrace();                     
}               

URLClassLoader urlLoader = new URLClassLoader(new java.net.URL[]{resourceURL});
java.util.ResourceBundle bundle = java.util.ResourceBundle.getBundle("mybundle.txt", 
                java.util.Locale.getDefault(), urlLoader );

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接