【问题标题】:How to read a file from jar in Java?如何从 Java 中的 jar 中读取文件?
【发布时间】:2011-03-23 03:19:17
【问题描述】:

我想读取一个 XML 文件,该文件位于我的类路径中包含的 jars 之一中。如何读取jar 中包含的任何文件?

【问题讨论】:

标签: java file-io jar


【解决方案1】:

如果您想从应用程序内部读取该文件,请使用:

InputStream input = getClass().getResourceAsStream("/classpath/to/my/file");

路径以“/”开头,但这不是文件系统中的路径,而是类路径中的路径。因此,如果您的文件位于类路径“org.xml”并且名为 myxml.xml,则您的路径看起来像“/org/xml/myxml.xml”。

InputStream 读取文件的内容。如果需要,您可以将其包装到 Reader 中。

希望对你有帮助。

【讨论】:

    【解决方案2】:

    啊,这是我最喜欢的科目之一。基本上有两种方法可以通过类路径加载资源:

    Class.getResourceAsStream(resource)
    

    ClassLoader.getResourceAsStream(resource)
    

    (还有其他方法涉及以类似方式获取资源的 URL,然后打开到它的连接,但这是两种直接方法)。

    在修改资源名称之后,第一种方法实际上委托给第二种方法。基本上有两种资源名称:绝对(例如“/path/to/resource/resource”)和相对(例如“资源”)。绝对路径以“/”开头。

    这是一个应该说明的例子。考虑一个类 com.example.A。考虑两个资源,一个位于类路径中的 /com/example/nested,另一个位于 /top。以下程序显示了访问这两种资源的九种可能方式:

    包 com.example; 公共类 A { 公共静态无效主要(字符串参数[]){ // Class.getResourceAsStream 对象资源 = A.class.getResourceAsStream("nested"); System.out.println("1: A.class nested=" + resource); 资源 = A.class.getResourceAsStream("/com/example/nested"); System.out.println("2: A.class /com/example/nested=" + 资源); 资源 = A.class.getResourceAsStream("top"); System.out.println("3: A.class top=" + resource); 资源 = A.class.getResourceAsStream("/top"); System.out.println("4: A.class /top=" + 资源); // ClassLoader.getResourceAsStream 类加载器 cl = A.class.getClassLoader(); 资源 = cl.getResourceAsStream("nested"); System.out.println("5: cl 嵌套=" + 资源); 资源 = cl.getResourceAsStream("/com/example/nested"); System.out.println("6: cl /com/example/nested=" + 资源); 资源 = cl.getResourceAsStream("com/example/nested"); System.out.println("7: cl com/example/nested=" + 资源); 资源 = cl.getResourceAsStream("top"); System.out.println("8: cl top=" + 资源); 资源 = cl.getResourceAsStream("/top"); System.out.println("9: cl /top=" + 资源); } }

    程序的输出是:

    1:A.class嵌套=java.io.BufferedInputStream@19821f 2:A.class /com/example/nested=java.io.BufferedInputStream@addbf1 3:A.class top=null 4:A.class /top=java.io.BufferedInputStream@42e816 5:cl嵌套=空 6: cl /com/example/nested=null 7:cl com/example/nested=java.io.BufferedInputStream@9304b1 8:cl top=java.io.BufferedInputStream@190d11 9: cl /top=null

    大多数事情都如你所愿。 Case-3 失败,因为类相对解析是针对类的,所以“top”表示“/com/example/top”,但“/top”表示它所说的。

    Case-5 失败,因为类加载器的相对解析是相对于类加载器的。但是,意外情况 6 也失败了:人们可能期望“/com/example/nested”能够正确解析。要通过类加载器访问嵌套资源,您需要使用 Case-7,即嵌套路径相对于类加载器的根目录。同样,Case-9 失败,但 Case-8 通过。

    记住:对于 java.lang.Class,getResourceAsStream() 确实委托给类加载器:

    公共输入流 getResourceAsStream(字符串名称){ 名称 = 解析名称(名称); 类加载器 cl = getClassLoader0(); 如果(cl==null){ // 一个系统类。 返回 ClassLoader.getSystemResourceAsStream(name); } 返回 cl.getResourceAsStream(name); }

    因此,resolveName() 的行为很重要。

    最后,既然是类加载器的行为加载了本质上控制getResourceAsStream()的类,并且类加载器通常是一个自定义加载器,那么资源加载规则可能会更加复杂。例如对于 Web 应用程序,从 Web 应用程序上下文中的 WEB-INF/classes 或 WEB-INF/lib 加载,而不是从隔离的其他 Web 应用程序加载。此外,表现良好的类加载器委托给父级,因此类路径中的重复资源可能无法使用此机制访问。

    【讨论】:

    【解决方案3】:

    JAR 基本上是一个 ZIP 文件,因此请照此处理。下面包含一个示例,说明如何从 WAR 文件中提取一个文件(也将其视为 ZIP 文件)并输出字符串内容。对于二进制文件,您需要修改提取过程,但有很多示例。

    public static void main(String args[]) {
        String relativeFilePath = "style/someCSSFile.css";
        String zipFilePath = "/someDirectory/someWarFile.war";
        String contents = readZipFile(zipFilePath,relativeFilePath);
        System.out.println(contents);
    }
    
    public static String readZipFile(String zipFilePath, String relativeFilePath) {
        try {
            ZipFile zipFile = new ZipFile(zipFilePath);
            Enumeration<? extends ZipEntry> e = zipFile.entries();
    
            while (e.hasMoreElements()) {
                ZipEntry entry = (ZipEntry) e.nextElement();
                // if the entry is not directory and matches relative file then extract it
                if (!entry.isDirectory() && entry.getName().equals(relativeFilePath)) {
                    BufferedInputStream bis = new BufferedInputStream(
                            zipFile.getInputStream(entry));
                    // Read the file
                        // With Apache Commons I/O
                     String fileContentsStr = IOUtils.toString(bis, "UTF-8");
    
                        // With Guava
                    //String fileContentsStr = new String(ByteStreams.toByteArray(bis),Charsets.UTF_8);
                    // close the input stream.
                    bis.close();
                    return fileContentsStr;
                } else {
                    continue;
                }
            }
        } catch (IOException e) {
            logger.error("IOError :" + e);
            e.printStackTrace();
        }
        return null;
    }
    

    在此示例中,我使用的是 Apache Commons I/O,如果您使用的是 Maven,这里是依赖项:

    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>
    

    【讨论】:

      【解决方案4】:

      首先检查您的类加载器。

      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
      
      if (classLoader == null) {
          classLoader = Class.class.getClassLoader();
      }
      
      classLoader.getResourceAsStream("xmlFileNameInJarFile.xml");
      
      // xml file location at xxx.jar
      // + folder
      // + folder
      // xmlFileNameInJarFile.xml
      

      【讨论】:

        【解决方案5】:

        为了完整起见,最近在 Jython 邮件列表中有一个 question,其中一个答案提到了这个线程。

        问题是如何从 Jython 中调用 .jar 文件中包含的 Python 脚本,建议的答案如下(使用“InputStream”,如上述答案之一所述:

        PythonInterpreter.execfile(InputStream)
        

        【讨论】:

          【解决方案6】:

          这也适用于春季

              ClassPathResource resource = new ClassPathResource("/file.txt", MainApplication.class); //resources folder
              InputStream inputStream = resource.getInputStream();
          
              File file = new File("file.txt");
              FileUtils.copyInputStreamToFile(inputStream, file);
          

          【讨论】:

            猜你喜欢
            • 2010-09-29
            相关资源
            最近更新 更多