Java 读取resources下的资源文件

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

准备工作

  • 新建一个spring-test 的maven项目,resources目录下创建测试文件conf.properties、city_code.json (json文件夹下)。

  • 添加pom依赖

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

<!-- spring web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.2.7.RELEASE</version>
</dependency>

<!-- commons-io -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

一、通过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;
}

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值