如何从Java jar文件中读取资源文件?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何从Java jar文件中读取资源文件?相关的知识,希望对你有一定的参考价值。

我正在尝试从作为桌面应用程序运行的单独jar中访问jar文件中的XML文件。我可以获取我需要的文件的URL,但是当我将它传递给FileReader(作为String)时,我得到一个FileNotFoundException,说“文件名,目录名或卷标语法不正确。”

作为参考,我可以毫不费力地从同一个jar中读取图像资源,并将URL传递给ImageIcon构造函数。这似乎表明我用来获取URL的方法是正确的。

URL url = getClass().getResource("/xxx/xxx/xxx/services.xml");
ServicesLoader jsl = new ServicesLoader( url.toString() );

在我的ServicesLoader类中

XMLReader xr = XMLReaderFactory.createXMLReader();
xr.setContentHandler( this );
xr.setErrorHandler( this );
xr.parse( new InputSource( new FileReader( filename )));

使用这种技术读取XML文件有什么问题?

答案

看起来你想使用java.lang.Class.getResourceAsStream(String),请参阅

http://java.sun.com/javase/6/docs/api/java/lang/Class.html#getResourceAsStream(java.lang.String)

另一答案

您不会说这是桌面应用程序还是Web应用程序。我会使用适当的ClassLoader中的getResourceAsStream()方法(如果它是桌面)或Context(如果它是Web应用程序)。

另一答案

看起来好像使用URL.toString结果作为FileReader构造函数的参数。 URL.toString有点破碎,而你通常应该使用url.toURI().toString()。在任何情况下,字符串都不是文件路径。

相反,你应该:

  • URL传递给ServicesLoader并让它命名为openStream或类似的。
  • 使用Class.getResourceAsStream并将流传过,可能在InputSource内。 (请记住检查空值,因为API有点乱。)
另一答案

问题是我在调用XMLReader的parse方法方面走得太远了。 parse方法接受一个I​​nputSource,因此没有理由使用FileReader。将上面代码的最后一行更改为

xr.parse( new InputSource( filename ));

工作得很好。

另一答案

我想指出一个问题是如果相同的资源在多个jar文件中。假设您要读取/org/node/foo.txt,但不是来自一个文件,而是来自每个jar文件。

我以前曾多次遇到同样的问题。我希望在JDK 7中有人会写一个类路径文件系统,但还没有。

Spring有Resource类,它允许你很好地加载类路径资源。

我写了一个小原型来解决从多个jar文件中读取资源这个问题。原型不处理每个边缘情况,但它确实处理查找jar文件中的目录中的资源。

我已经使用Stack Overflow很长一段时间了。这是我记得回答一个问题的第二个答案,如果我走得太久就会原谅我(这是我的本性)。

这是一个原型资源阅读器。原型没有强大的错误检查功能。

我有两个原型jar文件,我已经设置好了。

 <pre>
         <dependency>
              <groupId>invoke</groupId>
              <artifactId>invoke</artifactId>
              <version>1.0-SNAPSHOT</version>
          </dependency>

          <dependency>
               <groupId>node</groupId>
               <artifactId>node</artifactId>
               <version>1.0-SNAPSHOT</version>
          </dependency>

每个jar文件都有一个名为/ org / node /的文件,名为resource.txt。

这只是一个处理程序与classpath一样的原型://我的本地资源中也有一个resource.foo.txt用于这个项目。

它将它们全部拾取并打印出来。

   

    package com.foo;

    import java.io.File;
    import java.io.FileReader;
    import java.io.InputStreamReader;
    import java.io.Reader;
    import java.net.URI;
    import java.net.URL;
    import java.util.Enumeration;
    import java.util.zip.ZipEntry;
    import java.util.zip.ZipFile;

    /**
    * Prototype resource reader.
    * This prototype is devoid of error checking.
    *
    *
    * I have two prototype jar files that I have setup.
    * <pre>
    *             <dependency>
    *                  <groupId>invoke</groupId>
    *                  <artifactId>invoke</artifactId>
    *                  <version>1.0-SNAPSHOT</version>
    *              </dependency>
    *
    *              <dependency>
    *                   <groupId>node</groupId>
    *                   <artifactId>node</artifactId>
    *                   <version>1.0-SNAPSHOT</version>
    *              </dependency>
    * </pre>
    * The jar files each have a file under /org/node/ called resource.txt.
    * <br />
    * This is just a prototype of what a handler would look like with classpath://
    * I also have a resource.foo.txt in my local resources for this project.
    * <br />
    */
    public class ClasspathReader {

        public static void main(String[] args) throws Exception {

            /* This project includes two jar files that each have a resource located
               in /org/node/ called resource.txt.
             */


            /* 
              Name space is just a device I am using to see if a file in a dir
              starts with a name space. Think of namespace like a file extension 
              but it is the start of the file not the end.
            */
            String namespace = "resource";

            //someResource is classpath.
            String someResource = args.length > 0 ? args[0] :
                    //"classpath:///org/node/resource.txt";   It works with files
                    "classpath:///org/node/";                 //It also works with directories

            URI someResourceURI = URI.create(someResource);

            System.out.println("URI of resource = " + someResourceURI);

            someResource = someResourceURI.getPath();

            System.out.println("PATH of resource =" + someResource);

            boolean isDir = !someResource.endsWith(".txt");


            /** Classpath resource can never really start with a starting slash.
             * Logically they do, but in reality you have to strip it.
             * This is a known behavior of classpath resources.
             * It works with a slash unless the resource is in a jar file.
             * Bottom line, by stripping it, it always works.
             */
            if (someResource.startsWith("/")) {
                someResource = someResource.substring(1);
            }

              /* Use the ClassLoader to lookup all resources that have this name.
                 Look for all resources that match the location we are looking for. */
            Enumeration resources = null;

            /* Check the context classloader first. Always use this if available. */
            try {
                resources = 
                    Thread.currentThread().getContextClassLoader().getResources(someResource);
            } catch (Exception ex) {
                ex.printStackTrace();
            }

            if (resources == null || !resources.hasMoreElements()) {
                resources = ClasspathReader.class.getClassLoader().getResources(someResource);
            }

            //Now iterate over the URLs of the resources from the classpath
            while (resources.hasMoreElements()) {
                URL resource = resources.nextElement();


                /* if the resource is a file, it just means that we can use normal mechanism
                    to scan the directory.
                */
                if (resource.getProtocol().equals("file")) {
                    //if it is a file then we can handle it the normal way.
                    handleFile(resource, namespace);
                    continue;
                }

                System.out.println("Resource " + resource);

               /*

                 Split up the string that looks like this:
                 jar:file:/Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar!/org/node/
                 into
                    this /Users/rick/.m2/repository/invoke/invoke/1.0-SNAPSHOT/invoke-1.0-SNAPSHOT.jar
                 and this
                     /org/node/
                */
                String[] split = resource.toString().split(":");
                String[] split2 = split[2].split("!");
                String zipFileName = split2[0];
                String sresource = split2[1];

                System.out.printf("After split zip file name = %s," +
                        " 
resource in zip %s 
", zipFileName, sresource);


                /* Open up the zip file. */
                ZipFile zipFile = new ZipFile(zipFileName);


                /*  Iterate through the entries.  */
                Enumeration entries = zipFile.entries();

                while (entries.hasMoreElements()) {
                    ZipEntry entry = entries.nextElement();
                    /* If it is a directory, then skip it. */
                    if (entry.isDirectory()) {
                        continue;
                    }

                    String entryName = entry.getName();
                    System.out.printf("zip entry name %s

以上是关于如何从Java jar文件中读取资源文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何从Java jar文件中读取资源文件?

(转)java 从jar包中读取资源文件

怎么从外部读取jar包中的资源文件

Java从jar资源中读取文件

docker上的Spring启动无法从资源中读取属性文件,但可以在没有Docker的情况下使用java -jar

从 jar 中读取资源文件