Bootstrap

小白速通Spring基础——Spring Resource

Spring Resource

文件系统是编程不可避开的领域,因为我们总是有可能读文件、写文件。Spring Framework 作为完整的 Java 企业级解决方案,自然也有文件处理方案啦,那就是 spring Resource

在我们正式学习 Spring Resource 的时候,你还需要了解一下在Java 工程中文件的几种情况

  1. 文件在电脑某个位置,比如说 d:/mywork/a.doc

  2. 文件在工程目录下,比如说 mywork/toutiao.png

  3. 文件在工程的 src/main/resources目录下,我们在 Maven 的知识里介绍过这是 Maven 工程存放文件的地方

第一种和第二种情况都是使用 File 对象就可以读写啦,第三种情况比较特殊,因为 Maven 执行 package 的时候,会把resources日录下的文件一起打包进 jar 包里(我们之前提到过 jar 是 Java 的压缩文件)。

显然在第三种情况,用 File 对象是读取不到的,因为文件已经在 jar里啦。

可以使用

import java.io.File;

/**
 * Test
 */
public class Test {

  public static void main(String[] args) {

    File file = new File("mywork/readme.md");
    if(file.exists()){
      System.out.println("readme.md 文件存在");
    }

    File file2 = new File("src/main/resources/nani.jpeg");
    if(file2.exists()){
      System.out.println("nani.jpeg 文件存在");
    }

  }

}

大家看到控制台输出了两个文件,现在看起来两个文件都可以加载对把?但是呢,在企业中源代码是不可能和运行程序放在一起的,所以下面的工程才是真实的情况。

在这里插入图片描述

我们运行看一下,这时候你会发现 src/main/resources/nani.jpeg图片找不到啦
大家可以下载 app-1.0-SNAPSHoT.jar 到本地电脑,然后用解压缩软件解压看看文件

在这里插入图片描述

咦???解压后,我们刚才那个图片在文件夹根目录下呢?并且src/main/java这些都没有啦。
这就是 Java 文件系统和计算机文件系统的差异,工程目录最后是要编译成 jar 文件的,jar文件是从 包路径 开始的。Maven 工程编译后,会自动去掉 src/main/javasrc/main/resources
OK,那么现在的问题就是如何读取 jar 内部的文件呢

再提醒一下:这类文件一般我们都是存放在src/main/resources目录的,会自动打包到 jar 文件内

classpath

在 Java 内部当中,我们一般把文件路径称为 classpath,所以读取内部的文件就是从 classpath 内读取,classpath 指定的文件不能解析成 File 对象,但是可以解析成 InputStream,我们借助 Java lO 就可以读取出来了

classpath 类似虚拟目录,它的根目录是从/开始代表的是src/main/java或者src/main/resources
我们来看一下如何使用 classpath 读取文件,这次我们在 resources 目录下存放一个 data.json 文件。

Java 拥有很多丰富的第三方类库给我们使用,读取文件,我们可以使用 commons-io 这个库来,需要我们在 pom.xml 下添加依赖

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.6</version>
</dependency>
public class Test {

  public static void main(String[] args) {
    // 读取 classpath 的内容
    InputStream in = Test.class.getClassLoader().getResourceAsStream("data.json");
    // 使用 commons-io 库读取文本
    try {
      String content = IOUtils.toString(in, "utf-8");
      System.out.println(content);
    } catch (IOException e) {
      // IOUtils.toString 有可能会抛出异常,需要我们捕获一下
      e.printStackTrace();
    }
  }

}

重点代码:

InputStream in = Test.class.getClassLoader().getResourceAsStream("data.json");

这段代码的含义就是从 Java 运行的类加载器(ClassLoader)实例中查找文件, Test.class 指的当前的 Test.java 编译后的 Java class 文件。记住这个代码格式就好,以后可能会经常用到,如果你记不住那 copy 也可以

ps:我在实现过程中遇到这样一个问题,当file不在resources里面的之后显示空指针异常
在这里插入图片描述
当将file放置resources下时正常运行
在这里插入图片描述

了解了 classpath 概念后,我想你肯定在想 Spring Resource 能做啥呢?请记住一点,Spring 擅长的就是封装各种服务

在 Spring 当中定义了一个
org.springframework.core.io.Resource类来封装文件,这个类的优势在于可以支持普通的 File 也可以支持 classpath 文件。

并且在 Spring 中通过org.springframework.core.io.ResourceLoader服务来提供任意文件的读写,你可以在任意的 Spring Bean 中引入 ResourceLoader

@Autowired
private ResourceLoader loader;

现在让我们来看一下在 Spring 当中如何读取文件,我们创建一个自
己的 FileService

public interface FileService {

    String getContent(String name);

}

在看一下实现类

import fm.douban.service.FileService;
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;

@Service
public class FileServiceImpl implements FileService {

    @Autowired
    private ResourceLoader loader;

    @Override
    public String getContent(String name) {
        try {
            InputStream in = loader.getResource(name).getInputStream();
            return IOUtils.toString(in,"utf-8");
        } catch (IOException e) {
           return null;
        }
    }
}

在看一下这个服务的调用,在启动类中:

 FileService fileService = context.getBean(FileService.class);
  String content = fileService.getContent("classpath:data/urls.txt");
  System.out.println(content);

读取本地文件:

String content2 = fileService.getContent("file:mywork/readme.md");
System.out.println(content2);

这个 file:mywork/readme.md 代表的就是读取工程目录下文件mywork/readme.md

读取远程文件:

String content3 = fileService.getContent("https://blog.csdn.net/oageux/article/details/140505021?spm=1001.2014.3001.5501");
System.out.println(content3);
;