Web项目中应该经常有这样的需求,在maven项目的resources目录下放一些文件。比如一些配置文件,资源文件等。文件的读取方式有好几种方式,本文会对常用的读取方式做一个总结,并说明一下应该注意的地方。

准备工作新建一个spring-test 的maven项目,resources目录下创建测试文件conf.properties、city_code.json (json文件夹下)。添加pom依赖

使用 FileUtils、IOUtils等工具类,需要引入 commons-io jar包。

org.springframework.boot

spring-boot-starter-web

2.2.7.RELEASE

commons-io

commons-io

2.6

一、通过ClassLoader读取文件

ClassLoader.getResourceAsStream()获取文件输入流

public void loadPropertiesFile() throws IOException {

Properties properties = new Properties();

properties.load(this.getClass().getClassLoader().getResourceAsStream("conf.properties"));

log.info(properties.getProperty("file.max.size"));

}

ClassLoader.getResourceAsStream()获取文件的URL

public void loadJsonFile() throws IOException {

//获取文件的URL URL url = this.getClass().getClassLoader().getResource("json/city_code.json");

String content = FileUtils.readFileToString(new File(url.getPath()), StandardCharsets.UTF_8);

log.info(content);

}

二、通过Class读取文件

Class.getResourceAsStream()获取文件的输入流

public void loadPropertiesFile() throws IOException {

Properties properties = new Properties();

properties.load(this.getClass().getResourceAsStream("/conf.properties"));

log.info(properties.getProperty("file.max.size"));

}

Class.getResourceAsStream()获取文件的URL

public void loadJsonFile() throws IOException {

//获取文件的URL URL url = this.getClass().getResource("/json/city_code.json");

String content = FileUtils.readFileToString(new File(url.getPath()), StandardCharsets.UTF_8);

log.info(content);

}Class.getResourceAsStream() 和 ClassLoader.getResourceAsStream()有一个显著的区别,那就是前者加载的时候需要在文件路径前加一个 "/"。

三、Spring项目读取文件通过ResourceUtils.getFile()方法可以读取File

public void loadJsonFile() throws IOException {

//获取文件的URL File file = ResourceUtils.getFile("classpath:json/city_code.json");

String content = FileUtils.readFileToString(file, StandardCharsets.UTF_8);

log.info(content);

}通过ClassPathResource获取输入流ClassPathResource的构造参数path不会做绝对路径的区分,"json/city_code.json" 或者 "/json/city_code.json"都能成功获取文件的输入流。

@SneakyThrows

public void loadJsonFil1(){

ClassPathResource classPathResource = new ClassPathResource("json/city_code.json");

InputStream inputStream = classPathResource.getInputStream();

String content = IOUtils.toString(inputStream, StandardCharsets.UTF_8);

log.info(content);

}

四、如何区分要不要加"/"

在使用这些方式读取文件时,都需要传入一个文件路径。至于要不要在路径前加一个"/"时常让人感到很困惑,这里稍微做一个总结。如果通过Class来读取文件,那么路径需要以"/"开始。通过ClassLoader或者Spring提供的工具类读取文件其实都不需要在路径前加"/"。

五、总结

碰到这些问题最好的解决办法,就是去翻一翻源码,然后断点调试看看一个读取文件过程中各个变量的值。通过看源码的方式不但能解决问题,还能帮自己捋清楚这些读取文件方式到底有什么联系和区别。

这里摘一段Class类两个方法的源码,通过代码可以得到如下结论。Class和ClassLoader都可以读取文件,不过Class读取文件还是通过ClassLoader来实现的。

Class读取文件之前需要经过resolveName方法,resolveName会对文件路径进行解析。如果路径以"/"开始,则删除"/"。如果路径不是"/"开始,则会在文件路径前拼接包名前缀,然后用新的文件路径来读取文件。

public java.net.URL getResource(String name) {

name = resolveName(name);

ClassLoader cl = getClassLoader0();

if (cl==null) {

// A system class. return ClassLoader.getSystemResource(name);

}

return cl.getResource(name);

}

public InputStream getResourceAsStream(String name) {

name = resolveName(name);

ClassLoader cl = getClassLoader0();

if (cl==null) {

// A system class. return ClassLoader.getSystemResourceAsStream(name);

}

return cl.getResourceAsStream(name);

}

/*** Add a package name prefix if the name is not absolute Remove leading "/"* if name is absolute*/

private String resolveName(String name) {

if (name == null) {

return name;

}

if (!name.startsWith("/")) {

Class> c = this;

while (c.isArray()) {

c = c.getComponentType();

}

String baseName = c.getName();

int index = baseName.lastIndexOf('.');

if (index != -1) {

name = baseName.substring(0, index).replace('.', '/')

+"/"+name;

}

} else {

name = name.substring(1);

}

return name;

}

更多推荐