从 jar 中读取资源文件

我想从 jar 里像这样读一个资源:

File file;
file = new File(getClass().getResource("/file.txt").toURI());
BufferedReader 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,文件所在的文件夹位于类文件夹中。

443017 次浏览

与其试图将资源寻址为文件,不如让类加载器返回资源的InputStream,而不是通过getResourceAsStream:

try (InputStream in = getClass().getResourceAsStream("/file.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(in))) {
// Use resource
}

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

出现URI is not hierarchical是因为jar文件中资源的URI看起来像这样:file:/example.jar!/file.txt。你不能读取jar(一个zip文件)中的条目,就像它是一个普通的旧的文件

以下问题的答案很好地解释了这一点:

如果你想读取一个文件,我相信仍然有一个类似的解决方案:

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

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

  • 将文件放在与包名匹配的目录结构中(在提取.jar文件后,它应该与.class文件在同一目录中),然后使用getClass().getResourceAsStream("file.txt")访问它

  • 将文件放在根目录(在提取.jar文件后,它应该在根目录中),然后使用Thread.currentThread().getContextClassLoader().getResourceAsStream("file.txt")访问它

当jar作为插件使用时,第一个选项可能不起作用。

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

如果你正在使用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();
}

我以前遇到过这个问题,我为装载做了退路。基本上第一种方式在.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;
}
}

到目前为止(2017年12月),这是我发现的唯一解决方案,在IDE内外工作这两个

使用< em > PathMatchingResourcePatternResolver < / em >

注意:它也适用于spring-boot

在这个例子中,我读取了一些位于src / main /资源/ 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);
}

你可以使用类加载器从类路径中读取根路径(开头没有“/”)

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

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

getClass().getClassLoader() API doc说,

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

可能是如果你正在使用WildFly和你的web应用程序试试这个

request.getServletContext().getResource()返回资源url。这里request是ServletRequest的一个对象。

问题是某些第三方库需要文件路径名而不是输入流。大多数答案都没有提到这个问题。

在这种情况下,一种解决方法是将资源内容复制到临时文件中。下面的例子使用了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();
}

下面的代码使用Spring引导(kotlin):

val authReader = InputStreamReader(javaClass.getResourceAsStream("/file1.json"))

我找到了解决办法

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

将“Main”替换为您编写它的java类。将“path”替换为jar文件中的路径。

例如,如果你把State1.txt放在com.issac包中。如果你运行的是Linux或Mac,那么输入路径为“/com/issac/state/State1”。如果你运行的是Windows,那么输入路径为“\com\issac\state\State1”。除非出现“文件未找到”异常,否则不要将.txt扩展名添加到文件中。

这段代码在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 "";
}

就我而言,我终于做到了

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

最后我解决了错误:

String input_path = "resources\\file.txt";
        

input_path = input_path.replace("\\", "/");  // doesn't work with back slash
        

URL file_url = getClass().getClassLoader().getResource(input_path);
String file_path = new URI(file_url.toString().replace(" ","%20")).getSchemeSpecificPart();
InputStream file_inputStream = file_url.openStream();