从jar中读取资源文件

时间:2013-12-05 00:48:51

标签: java file jar resources embedded-resource

我想从我的jar中读取资源:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferredReader reader = new BufferedReader(new FileReader(file));

//Read the file

并且在Eclipse中运行它时工作正常,但是如果我将它导出到jar,那么运行它就会出现IllegalArgumentException:

Exception in thread "Thread-2"
java.lang.IllegalArgumentException: URI is not hierarchical

而且我真的不知道为什么,但是我发现如果我改变了一些测试

file = new File(getClass().getResource("/file.txt").toURI());

file = new File(getClass().getResource("/folder/file.txt").toURI());

然后它正好相反(它在jar中工作但不是eclipse)。

我正在使用Eclipse,我文件夹中的文件夹位于类文件夹中。

17 个答案:

答案 0 :(得分:312)

而不是尝试将资源作为File来处理,而只是让ClassLoader通过InputStream为资源返回getResourceAsStream

InputStream in = getClass().getResourceAsStream("/file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

只要file.txt资源在类路径上可用,那么无论file.txt资源是在classes/目录中还是在{{{}内,此方法都将以相同的方式工作1}}。

发生jar是因为jar文件中资源的URI看起来像这样:URI is not hierarchical。您无法阅读file:/example.jar!/file.txtjar文件)中的条目,就像它是普通的File一样。

以下答案解释了这一点:

答案 1 :(得分:15)

要访问jar中的文件,您有两个选择:

  • 将文件放在与您的包名匹配的目录结构中(在解压缩.jar文件后,它应该与.class文件位于同一目录中),然后使用getClass().getResourceAsStream("file.txt")

    <访问它/ LI>
  • 将文件放在根目录中(在解压缩.jar文件后,它应该在根目录中),然后使用Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")

  • 访问它

当jar用作插件时,第一个选项可能无效。

答案 2 :(得分:5)

如果您想阅读文件,我相信仍然有类似的解决方案:

    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("file/test.xml").getFile());

答案 3 :(得分:2)

之前我遇到过这个问题而且我做了后备加载方式。基本上第一种方式在.jar文件中工作,第二种方式在eclipse或其他IDE中工作。

public class MyClass {

    public static InputStream accessFile() {
        String resource = "my-file-located-in-resources.txt";

        // this is the path within the jar file
        InputStream input = MyClass.class.getResourceAsStream("/resources/" + resource);
        if (input == null) {
            // this is how we load file within editor (eg eclipse)
            input = MyClass.class.getClassLoader().getResourceAsStream(resource);
        }

        return input;
    }
}

答案 4 :(得分:2)

对于我来说,我终于做到了

import java.lang.Thread;
import java.io.BufferedReader;
import java.io.InputStreamReader;

final BufferedReader in = new BufferedReader(new InputStreamReader(
      Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt"))
); // no initial slash in file.txt

答案 5 :(得分:1)

确保使用正确的分隔符。我用/替换了相对路径中的所有File.separator。这在IDE中运行良好,但在构建JAR中不起作用。

答案 6 :(得分:1)

到目前为止(2017年12月),这是我发现的唯一可以在IDE内部和外部使用 的解决方案。

使用 PathMatchingResourcePatternResolver

注意:它也适用于spring-boot

在此示例中,我正在阅读 src / main / resources / my_folder 中的一些文件:

try {
    // Get all the files under this inner resource folder: my_folder
    String scannedPackage = "my_folder/*";
    PathMatchingResourcePatternResolver scanner = new PathMatchingResourcePatternResolver();
    Resource[] resources = scanner.getResources(scannedPackage);

    if (resources == null || resources.length == 0)
        log.warn("Warning: could not find any resources in this scanned package: " + scannedPackage);
    else {
        for (Resource resource : resources) {
            log.info(resource.getFilename());
            // Read the file content (I used BufferedReader, but there are other solutions for that):
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(resource.getInputStream()));
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                // ...
                // ...                      
            }
            bufferedReader.close();
        }
    }
} catch (Exception e) {
    throw new Exception("Failed to read the resources folder: " + e.getMessage(), e);
}

答案 7 :(得分:1)

我找到了解决方法

BufferedReader br = new BufferedReader(new InputStreamReader(Main.class.getResourceAsStream(path)));

用您在其中编写的Java类替换“ Main”。用jar文件中的路径替换“ path”。

例如,如果将State1.txt放入com.issac.state包中,则如果运行Linux或Mac,则将路径键入为“ / com / issac / state / State1”。如果运行Windows,则将路径键入为“ \ com \ issac \ state \ State1”。除非发生“找不到文件”异常,否则请勿将.txt扩展名添加到文件中。

答案 8 :(得分:0)

我在这里尝试了所有提到的东西,所以让我重新表述我的问题,

javax.xml.ws.Service service =
          javax.xml.ws.Service
              .create(new File("src/main/resources/wsdl/iridium/iridium.wsdl").toURI().toURL(),
                  new QName(WWW_IRIDIUM_COM, IWS));

这个 wsdl 文件在我将使用 jar 的 java 项目中,所以当我将这个 jar 导入到另一个项目时,我得到文件未找到异常,因为该项目试图查看其资源文件夹以启动弹簧启动应用程序。

我需要做哪些更改才能使这个项目以这个 jar 作为依赖项开始。

答案 9 :(得分:0)

此代码在Eclipse和Exported Runnable JAR中均有效

private String writeResourceToFile(String resourceName) throws IOException {
    File outFile = new File(certPath + File.separator + resourceName);

    if (outFile.isFile())
        return outFile.getAbsolutePath();
    
    InputStream resourceStream = null;
    
    // Java: In caso di JAR dentro il JAR applicativo 
    URLClassLoader urlClassLoader = (URLClassLoader)Cypher.class.getClassLoader();
    URL url = urlClassLoader.findResource(resourceName);
    if (url != null) {
        URLConnection conn = url.openConnection();
        if (conn != null) {
            resourceStream = conn.getInputStream();
        }
    }
    
    if (resourceStream != null) {
        Files.copy(resourceStream, outFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
        return outFile.getAbsolutePath();
    } else {
        System.out.println("Embedded Resource " + resourceName + " not found.");
    }
    
    return "";
}   

答案 10 :(得分:0)

以下代码可用于Spring Boot(kotlin):

redeploy-certificates.yml

答案 11 :(得分:0)

问题在于某些第三方库需要文件路径名而不是输入流。大多数答案都不能解决这个问题。

在这种情况下,一种解决方法是将资源内容复制到一个临时文件中。以下示例使用jUnit的TemporaryFolder

    private List<String> decomposePath(String path){
        List<String> reversed = Lists.newArrayList();
        File currFile = new File(path);
        while(currFile != null){
            reversed.add(currFile.getName());
            currFile = currFile.getParentFile();
        }
        return Lists.reverse(reversed);
    }

    private String writeResourceToFile(String resourceName) throws IOException {
        ClassLoader loader = getClass().getClassLoader();
        InputStream configStream = loader.getResourceAsStream(resourceName);
        List<String> pathComponents = decomposePath(resourceName);
        folder.newFolder(pathComponents.subList(0, pathComponents.size() - 1).toArray(new String[0]));
        File tmpFile = folder.newFile(resourceName);
        Files.copy(configStream, tmpFile.toPath(), REPLACE_EXISTING);
        return tmpFile.getAbsolutePath();
    }

答案 12 :(得分:0)

出于某种原因,当我将Web应用程序部署到WildFly 14时,classLoader.getResource()总是返回null。从getClass().getClassLoader()Thread.currentThread().getContextClassLoader()获取classLoader返回null。

getClass().getClassLoader() API文档说,

“返回该类的类加载器。某些实现可能使用null来表示引导类加载器。如果此类是由引导类加载器加载的,则此方法在此类实现中将返回null。”

可能是如果您使用的是WildFly,并且您的网络应用程序尝试了此

request.getServletContext().getResource()返回了资源URL。这里的请求是ServletRequest的对象。

答案 13 :(得分:0)

我认为这在Java中也应该适用。我使用的以下代码是使用kotlin。

val resource = Thread.currentThread().contextClassLoader.getResource('resources.txt')

答案 14 :(得分:0)

经过大量的Java挖掘后,唯一对我有用的解决方案是手动读取jar文件本身,除非你在开发环境(IDE)中:

/** @return The root folder or jar file that the class loader loaded from */
public static final File getClasspathFile() {
    return new File(YourMainClass.class.getProtectionDomain().getCodeSource().getLocation().getFile());
}

/** @param resource The path to the resource
 * @return An InputStream containing the resource's contents, or
 *         <b><code>null</code></b> if the resource does not exist */
public static final InputStream getResourceAsStream(String resource) {
    resource = resource.startsWith("/") ? resource : "/" + resource;
    if(getClasspathFile().isDirectory()) {//Development environment:
        return YourMainClass.class.getResourceAsStream(resource);
    }
    final String res = resource;//Jar or exe:
    return AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
        @SuppressWarnings("resource")
        @Override
        public InputStream run() {
            try {
                final JarFile jar = new JarFile(getClasspathFile());
                String resource = res.startsWith("/") ? res.substring(1) : res;
                if(resource.endsWith("/")) {//Directory; list direct contents:(Mimics normal getResourceAsStream("someFolder/") behaviour)
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    Enumeration<JarEntry> entries = jar.entries();
                    while(entries.hasMoreElements()) {
                        JarEntry entry = entries.nextElement();
                        if(entry.getName().startsWith(resource) && entry.getName().length() > resource.length()) {
                            String name = entry.getName().substring(resource.length());
                            if(name.contains("/") ? (name.endsWith("/") && (name.indexOf("/") == name.lastIndexOf("/"))) : true) {//If it's a folder, we don't want the children's folders, only the parent folder's children!
                                name = name.endsWith("/") ? name.substring(0, name.length() - 1) : name;
                                baos.write(name.getBytes(StandardCharsets.UTF_8));
                                baos.write('\r');
                                baos.write('\n');
                            }
                        }
                    }
                    jar.close();
                    return new ByteArrayInputStream(baos.toByteArray());
                }
                JarEntry entry = jar.getJarEntry(resource);
                InputStream in = entry != null ? jar.getInputStream(entry) : null;
                if(in == null) {
                    jar.close();
                    return in;
                }
                final InputStream stream = in;//Don't manage 'jar' with try-with-resources or close jar until the
                return new InputStream() {//returned stream is closed(closing the jar closes all associated InputStreams):
                    @Override
                    public int read() throws IOException {
                        return stream.read();
                    }

                    @Override
                    public int read(byte b[]) throws IOException {
                        return stream.read(b);
                    }

                    @Override
                    public int read(byte b[], int off, int len) throws IOException {
                        return stream.read(b, off, len);
                    }

                    @Override
                    public long skip(long n) throws IOException {
                        return stream.skip(n);
                    }

                    @Override
                    public int available() throws IOException {
                        return stream.available();
                    }

                    @Override
                    public void close() throws IOException {
                        try {
                            jar.close();
                        } catch(IOException ignored) {
                        }
                        stream.close();
                    }

                    @Override
                    public synchronized void mark(int readlimit) {
                        stream.mark(readlimit);
                    }

                    @Override
                    public synchronized void reset() throws IOException {
                        stream.reset();
                    }

                    @Override
                    public boolean markSupported() {
                        return stream.markSupported();
                    }
                };
            } catch(Throwable e) {
                e.printStackTrace();
                return null;
            }
        }
    });
}

注意:上面的代码似乎只对jar文件有效,如果它在主类中。我不确定为什么。

答案 15 :(得分:-1)

您可以使用类加载器,该类加载器将从类路径读取为ROOT路径(开头不带“ /”)

InputStream in = getClass().getClassLoader().getResourceAsStream("file.txt"); 
BufferedReader reader = new BufferedReader(new InputStreamReader(in));

答案 16 :(得分:-1)

如果您使用的是spring,那么您可以使用以下方法从src / main / resources中读取文件:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import org.springframework.core.io.ClassPathResource;

  public String readFileToString(String path) throws IOException {

    StringBuilder resultBuilder = new StringBuilder("");
    ClassPathResource resource = new ClassPathResource(path);

    try (
        InputStream inputStream = resource.getInputStream();
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream))) {

      String line;

      while ((line = bufferedReader.readLine()) != null) {
        resultBuilder.append(line);
      }

    }

    return resultBuilder.toString();
  }