在Java中,我们可以使用getResourceAsStream
或getResource
从resources
文件夹或类路径的根读取一个或多个文件。
getResourceAsStream
方法返回一个InputStream
.
// the stream holding the file content
InputStream is = getClass().getClassLoader().getResourceAsStream("file.txt");
// for static access, uses the class name directly
InputStream is = JavaClassName.class.getClassLoader().getResourceAsStream("file.txt");
getResource
方法返回 一个URL
,通常将其转换为File
;在 JAR 文件中不起作用。
// get the file url, not working in JAR file.
URL resource = getClass().getClassLoader().getResource("file.txt");
if (resource == null) {
throw new IllegalArgumentException("file not found!");
} else {
// failed if files have whitespaces or special characters
//return new File(resource.getFile());
return new File(resource.toURI());
}
// for static access
// URL resource = JavaClassName.class.getClassLoader().getResource("fileName");
1. 资源文件夹中的文件
1.1 查看src/main/resources
中的文件,稍后我们将访问这些文件并打印出文件内容。
src/main/resources/database.properties
datasource.url=jdbc:mysql://localhost/favtuts?useSSL=false
datasource.username=root
datasource.password=password
datasource.driver-class-name=com.mysql.jdbc.Driver
src/main/resources/json/file1.json
{
"name": "favtuts",
"age": 38
}
src/main/resources/json/file2.json
{
"name": "jack",
"age": 40
}
src/main/resources/json/sub/subfile1.json
{
"name": "sub",
"age": 99
}
1.2 默认情况下,构建工具(如 Maven、Gradle 或常见的 Java 实践)会将所有文件从src/main/resources
复制到target/classes
或build/classes
的根目录。因此,当我们尝试从src/main/resources
读取文件时,我们从项目类路径的根读取该文件。
1.3 下面是一个 JAR 文件结构。通常,resources
文件夹中的文件将复制到类路径的根目录。
$ jar -tf target/example.jar
META-INF/MANIFEST.MF
META-INF/
json/
json/sub/
json/file2.json
json/sub/subfile1.json
json/file1.json
database.properties
com/
com/favtuts/
com/favtuts/io/
com/favtuts/io/utils/
//...
2. 从资源文件夹中获取文件。
2.1 下面的示例演示如何使用getResourceAsStream
和getResource
方法从resources
文件夹中读取json/file1.json
文件并打印出文件内容。
注意
-
getResource
方法在 JAR 文件中不起作用。 -
getResourceAsStream
方法在任何地方都有效。
FileResourcesUtils.java
package com.favtuts.io.utils;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
public class FileResourcesUtils {
public static void main(String[] args) throws Exception {
FileResourcesUtils app = new FileResourcesUtils();
//String fileName = "database.properties";
String fileName = "json/file1.json";
System.out.println("getResourceAsStream : " + fileName);
InputStream is = app.getFileFromResourceAsStream(fileName);
printInputStream(is);
System.out.println("\ngetResource : " + fileName);
File file = app.getFileFromResource(fileName);
printFile(file);
}
// get a file from the resources folder
// works everywhere, IDEA, unit test and JAR file.
private InputStream getFileFromResourceAsStream(String fileName) {
// The class loader that loaded the class
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(fileName);
// the stream holding the file content
if (inputStream == null) {
throw new IllegalArgumentException("file not found! " + fileName);
} else {
return inputStream;
}
}
/*
The resource URL is not working in the JAR
If we try to access a file that is inside a JAR,
It throws NoSuchFileException (linux), InvalidPathException (Windows)
Resource URL Sample: file:java-io.jar!/json/file1.json
*/
private File getFileFromResource(String fileName) throws URISyntaxException{
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(fileName);
if (resource == null) {
throw new IllegalArgumentException("file not found! " + fileName);
} else {
// failed if files have whitespaces or special characters
//return new File(resource.getFile());
return new File(resource.toURI());
}
}
// print input stream
private static void printInputStream(InputStream is) {
try (InputStreamReader streamReader =
new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
// print a file
private static void printFile(File file) {
List<String> lines;
try {
lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
getResourceAsStream : json/file1.json
{
"name": "favtuts",
"age": 38
}
getResource : json/file1.json
{
"name": "favtuts",
"age": 38
}
2.2 现在,我们将项目打包到一个JAR文件中并运行它;这一次,getResource
将失败并返回NoSuchFileException
或InvalidPathException
。我们无法通过资源 URL 读取 JAR 文件中的文件。
在 Linux (Ubuntu) 上运行 JAR 文件,它会引发NoSuchFileException
。
$ mvn clean package
$ java -jar target/java-io.jar
$ jar tf target/java-io.jar | more
$ java -cp target/java-io.jar com.favtuts.io.utils.FileResourcesUtils
getResourceAsStream : json/file1.json
{
"name": "favtuts",
"age": 38
}
# for new File(resource.getFile());
getResource : json/file1.json
java.nio.file.NoSuchFileException: file:/home/favtuts/projects/core-java/java-io/target/java-io.jar!/json/file1.json
at java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
at java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
at java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:219)
at java.base/java.nio.file.Files.newByteChannel(Files.java:370)
at java.base/java.nio.file.Files.newByteChannel(Files.java:421)
at java.base/java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:420)
at java.base/java.nio.file.Files.newInputStream(Files.java:155)
at java.base/java.nio.file.Files.newBufferedReader(Files.java:2838)
at java.base/java.nio.file.Files.readAllLines(Files.java:3329)
at com.favtuts.io.utils.FileResourcesUtils.printFile(FileResourcesUtils.java:135)
at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:24)
# for new File(resource.toURI());
getResource : json/file1.json
Exception in thread "main" java.lang.IllegalArgumentException: URI is not hierarchical
at java.base/java.io.File.<init>(File.java:420)
at com.favtuts.io.utils.FileResourcesUtils.getFileFromResource(FileResourcesUtils.java:112)
at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:29)
您可能会收到以下消息
no main manifest attribute, in "java-io.jar"
要解决此问题,请阅读堆栈溢出:无法执行 jar 文件:“无主清单属性”中的指南
对于 Maven,我们应该使用在 pom 中定义的 maven-jar 插件.xml:
<!-- Make this jar executable -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<excludes>
<exclude>**/log4j.properties</exclude>
</excludes>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.favtuts.io.utils.FileResourcesUtils</mainClass>
<classpathPrefix>dependency-jars/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
附言:此示例使用 Maven 插件创建 JAR
文件。
要检查 jar 文件中的主类,请执行以下操作:
$ jar tf target/java-io.jar | more
3. 从资源文件夹获取文件 – 单元测试
3.1 我们将测试资源放在单元测试的src/test/resources
文件夹中。通常,测试资源中的文件将复制到target/test-classes
文件夹。
src/test/resources/json/file1.json
{
"name": "unit test",
"age": 38
}
src/test/resources/database.properties.properties
datasource.url=jdbc:mysql://localhost/test?useSSL=false
datasource.username=test
datasource.password=password
datasource.driver-class-name=com.mysql.jdbc.Driver
3.2 它的工作方式与我们从src/main/resources
中读取文件的方式相同。我们使用相同的getResourceAsStream
和getResource
方法从 src/test/resources
读取文件.
FileResourcesTest.java
package com.favtuts.io;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
// Unit test class
public class FileResourcesTest {
@DisplayName("Test loading a JSON file")
@Test
void loadJSONTest() {
String fileName = "json/file1.json";
ClassLoader classLoader = getClass().getClassLoader();
try (InputStream inputStream = classLoader.getResourceAsStream(fileName);
InputStreamReader streamReader = new InputStreamReader(inputStream, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)
) {
String line;
while((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
@DisplayName("Test loading a properties file")
@Test
void loadPropTest() throws IOException, URISyntaxException {
String fileName = "database.properties";
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(fileName);
if (resource == null) {
throw new IllegalArgumentException("file not found! " + fileName);
}
//File file = new File(resource.getFile());
File file = new File(resource.toURI());
List<String> lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
lines.forEach(System.out::println);
}
}
输出
{
"name": "unit test",
"age": 38
}
datasource.url=jdbc:mysql://localhost/test?useSSL=false
datasource.username=test
datasource.password=password
datasource.driver-class-name=com.mysql.jdbc.Driver
4. 从资源文件夹中获取所有文件。(非 JAR 环境)
如果我们不知道确切的文件名,并且想要读取所有文件,包括资源文件夹中的子文件夹文件,我们可以使用NIO Files.walk
轻松访问和读取文件。
4.1 以下示例使用Files.walk
从src/main/resources/json
文件夹中读取所有文件:
FileResourcesUtils.java
p
package com.favtuts.io.utils;
import java.io.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Collectors;
public class FileResourcesUtils {
public static void main(String[] args) throws IOException {
FileResourcesUtils app = new FileResourcesUtils();
// read all files from a resources folder
try {
// files from src/main/resources/json
List<File> result = app.getAllFilesFromResource("json");
for (File file : result) {
System.out.println("file : " + file);
printFile(file);
}
} catch (URISyntaxException | IOException e) {
e.printStackTrace();
}
}
private List<File> getAllFilesFromResource(String folder)
throws URISyntaxException, IOException {
ClassLoader classLoader = getClass().getClassLoader();
URL resource = classLoader.getResource(folder);
// dun walk the root path, we will walk all the classes
List<File> collect = Files.walk(Paths.get(resource.toURI()))
.filter(Files::isRegularFile)
.map(x -> x.toFile())
.collect(Collectors.toList());
return collect;
}
// print a file
private static void printFile(File file) {
List<String> lines;
try {
lines = Files.readAllLines(file.toPath(), StandardCharsets.UTF_8);
lines.forEach(System.out::println);
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
file : /home/favtuts/projects/core-java/java-io/target/classes/json/file1.json
{
"name": "favtuts",
"age": 38
}
file : /home/favtuts/projects/core-java/java-io/target/classes/json/file2.json
{
"name": "jack",
"age": 40
}
file : /home/favtuts/projects/core-java/java-io/target/classes/json/sub/subfile1.json
{
"name": "sub",
"age": 99
}
4.2 但是,示例 4.1 中的标准Files.walk
无法直接访问 JAR 文件中的文件,请尝试在 JAR 环境中运行示例 4.1,然后它会引发FileSystemNotFoundException
。
$ mvn clean package
$ java -jar target/java-io.jar
Exception in thread "main" java.nio.file.FileSystemNotFoundException
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getFileSystem(ZipFileSystemProvider.java:169)
at jdk.zipfs/jdk.nio.zipfs.ZipFileSystemProvider.getPath(ZipFileSystemProvider.java:155)
at java.base/java.nio.file.Path.of(Path.java:208)
at java.base/java.nio.file.Paths.get(Paths.java:97)
at com.favtuts.io.utils.FileResourcesUtils.getAllFilesFromResource(FileResourcesUtils.java:128)
at com.favtuts.io.utils.FileResourcesUtils.main(FileResourcesUtils.java:35)
5. 从资源文件夹中获取所有文件。(JAR 版本)
5.1 此示例演示Files.walk
如何通过FileSystems
和jar:file:xxx.jar
URI读取 JAR 文件内的文件夹 。
这个想法是:
- 文件使用
FileSystems
在 JAR 文件中遍历文件夹,并获取所有文件名,请参见getPathsFromResourceJAR()
- 循环所有文件名,访问并打印每个文件,如示例 2.1,请参阅
getFileFromResourceAsStream()
FileResourcesUtils.java
package com.favtuts.io.utils;
import java.io.*;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class FileResourcesUtils {
public static void main(String[] args) throws IOException {
FileResourcesUtils app = new FileResourcesUtils();
// Sample 3 - read all files from a resources folder (JAR version)
try {
// get paths from src/main/resources/json
List<Path> result = app.getPathsFromResourceJAR("json");
for (Path path : result) {
System.out.println("Path : " + path);
String filePathInJAR = path.toString();
// Windows will returns /json/file1.json, cut the first /
// the correct path should be json/file1.json
if (filePathInJAR.startsWith("/")) {
filePathInJAR = filePathInJAR.substring(1, filePathInJAR.length());
}
System.out.println("filePathInJAR : " + filePathInJAR);
// read a file from resource folder
InputStream is = app.getFileFromResourceAsStream(filePathInJAR);
printInputStream(is);
}
} catch (URISyntaxException | IOException e) {
e.printStackTrace();
}
}
// get a file from the resources folder
// works everywhere, IDEA, unit test and JAR file.
private InputStream getFileFromResourceAsStream(String fileName) {
// The class loader that loaded the class
ClassLoader classLoader = getClass().getClassLoader();
InputStream inputStream = classLoader.getResourceAsStream(fileName);
// the stream holding the file content
if (inputStream == null) {
throw new IllegalArgumentException("file not found! " + fileName);
} else {
return inputStream;
}
}
// Get all paths from a folder that inside the JAR file
private List<Path> getPathsFromResourceJAR(String folder)
throws URISyntaxException, IOException {
List<Path> result;
// get path of the current running JAR
String jarPath = getClass().getProtectionDomain()
.getCodeSource()
.getLocation()
.toURI()
.getPath();
System.out.println("JAR Path :" + jarPath);
// file walks JAR
URI uri = URI.create("jar:file:" + jarPath);
try (FileSystem fs = FileSystems.newFileSystem(uri, Collections.emptyMap())) {
result = Files.walk(fs.getPath(folder))
.filter(Files::isRegularFile)
.collect(Collectors.toList());
}
return result;
}
// print input stream
private static void printInputStream(InputStream is) {
try (InputStreamReader streamReader = new InputStreamReader(is, StandardCharsets.UTF_8);
BufferedReader reader = new BufferedReader(streamReader)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出
$ java -jar target/java-io.jar
JAR Path :/C:/Users/favtuts/projects/core-java/java-io/target/java-io.jar
Path : /json/file2.json
filePathInJAR : json/file2.json
{
"name": "jack",
"age": 40
}
Path : /json/file1.json
filePathInJAR : json/file1.json
{
"name": "favtuts",
"age": 38
}
Path : /json/sub/subfile1.json
filePathInJAR : json/sub/subfile1.json
{
"name": "sub",
"age": 99
}
下载源代码
$ git clone https://github.com/favtuts/java-core-tutorials-examples
$ cd java-io