Springboot读取Jar包内文件

情景列举

  • 文件路径分为相对路径和绝对路径;

  • 现实中环境分为maven+Idea集成开发环境、windows运行环境、Linux运行环境;

  • 包的形式有jar包和war包;

下面研究:

  • 在不同环境下,不同包形式,不同路径形式的文件读取方式。(暂时只测试了springboot-jar包)

  • 读取方式采用:普通Java中ClassLoader方式 和 Spring中ResourceUtils/ResourceLoader两种

  • 基本的java中InputStream + File文件读取方式测试失败,下面不予赘述

# [1]相对路径
./config/hello.txt

# [2]相对路径,类似于[1]中的相对路径
config/hello.txt

# [3] 绝对路径(一般指Linux下,# win下一般会带着盘符)
/config/hello.txt
/D:/config/hello.txt
.  代表目前所在的目录 
.. 代表上一层目录
/  代表根目录

解决问题

如何读取到封装在Jar包里面的(在指定文件夹下面/resources/config/*)相关配置或者证书文件,而不是去读外部系统文件或者挂载到分布式存储系统的文件(k8s)等。

// 无论何种环境,禁止使用以下代码块(不允许使用File来操作)
File file = resource.getFile();
System.out.println(file.getPath());
FileInputStream inputStream = new FileInputStream(file);

 方法一:利用单纯地ClassLoader来操作,但是对于文件路径有局限性(具体看运行环境),其中默认的ClassLoaderTomcatEmbeddedWebappClassLoader

@RestController
@RequestMapping("/java")
public class JustTestJavaController {

    @GetMapping("/testDefaultClassLoader")
    public String testDefaultClassLoader(@RequestParam(required = false) String path) {
        StringBuilder sb = new StringBuilder();
        try {
            if (path == null) {
                // path = "config/hello.txt";
                path = "./config/hello.txt";
            }
            System.out.println(path);
            ClassLoader defaultClassLoader = ClassUtils.getDefaultClassLoader();
            assert defaultClassLoader != null;
            URL resource = defaultClassLoader.getResource(path);
            assert resource != null;
            InputStream inputStream = resource.openStream();
            int len;
            byte[] buf = new byte[1024];
            while ((len = inputStream.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }
            inputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return sb.toString();
    }

    @GetMapping("/testDemoClassLoader")
    public String testDemoClassLoader(@RequestParam(required = false) String path) {
        StringBuilder sb = new StringBuilder();
        try {
            if (path == null) {
                // path = "config/hello.txt";
                path = "./config/hello.txt";
            }
            System.out.println(path);
            ClassLoader classLoader = DemoApplication.class.getClassLoader();
            URL resource = classLoader.getResource(path);
            assert resource != null;
            InputStream inputStream = resource.openStream();
            int len;
            byte[] buf = new byte[1024];
            while ((len = inputStream.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }
            inputStream.close();

        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return sb.toString();
    }
}

方法二:利用Spring的ResourceLoader可以获取到,类似于mybatis读取mapper.xml

# mybatis部分配置项
mybatis.mapper-locations=classpath*:/mapper/**/*.xml
// mybatis部分源码
private static final ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();

private String[] mapperLocations;

public Resource[] resolveMapperLocations() {
    return Stream.of(Optional.ofNullable(this.mapperLocations).orElse(new String[0]))
        .flatMap(location -> Stream.of(getResources(location))).toArray(Resource[]::new);
}

private Resource[] getResources(String location) {
    try {
        return resourceResolver.getResources(location);
    } catch (IOException e) {
        return new Resource[0];
    }
}

其中ResourceLoader构造方法中,可以传入ClassLoader来指定类加载器,一般springboot项目指定启动类DemoApplication.class.getClassLoader()。若果不指定,大概率是使用TomcatEmbeddedWebappClassLoader。所以,spring实际上使用的依旧是ClassLoader的方法来加载资源,只不过在path处理上更加灵活,小部分支持路径正则匹配(SpringPathMatcherUtil

@RestController
@RequestMapping("/spring")
public class JustTestSpringController {

    ResourceLoader singleLoader = new PathMatchingResourcePatternResolver(DemoApplication.class.getClassLoader());
    ResourcePatternResolver batchLoader = new PathMatchingResourcePatternResolver();

    @GetMapping("/testInputPathSingle")
    public String testInputPathSingle(@RequestParam(required = false) String path) {
        StringBuilder sb = new StringBuilder();
        try {
            if (path == null) {
                // Thats all ok
                //path = "/config/hello.txt";
                //path = "config/hello.txt";
                //path = "./config/hello.txt";
                //path = "classpath:config/hello.txt";
                //path = "classpath:./config/hello.txt";
                path = "classpath:/config/hello.txt";
            }
            System.out.println(path);
            Resource resource = singleLoader.getResource(path);
            InputStream inputStream = resource.getInputStream();
            int len;
            byte[] buf = new byte[1024];
            while ((len = inputStream.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return sb.toString();
    }

    @GetMapping("/testInputPathBatch")
    public String testInputPathBatch(@RequestParam(required = false) String path) {
        StringBuilder sb = new StringBuilder();
        try {
            if (path == null) {
                // Thats all ok
                //path = "/config/hello.txt";
                //path = "config/hello.txt";
                //path = "./config/hello.txt";
                //path = "classpath*:config/hello.txt";
                //path = "classpath*:./config/hello.txt";
                path = "classpath*:/config/hello.txt";

                // and Support regular expressions
                //path = "classpath*:/config/*.txt";
            }
            System.out.println(path);
            Resource[] resources = batchLoader.getResources(path);
            for (Resource res : resources) {
                InputStream inputStream = res.getInputStream();
                int len;
                byte[] buf = new byte[1024];
                while ((len = inputStream.read(buf)) != -1) {
                    sb.append(new String(buf, 0, len));
                }
                inputStream.close();
                sb.append(":");
            }
        } catch (IOException e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return sb.toString();
    }
}

 

环境测试

本地IDEA+Maven集成开发环境

/* 方法一 */
// path = "/config/hello.txt"  读取失败

// path = "config/hello.txt"   读取成功
// path = "./config/hello.txt" 读取成功
/* 方法二 */
// 列举的路径中,所有情况下都可以

Windows服务机

/* 方法一 */
// path = "/config/hello.txt"  读取成功
// path = "config/hello.txt"   读取成功

// path = "./config/hello.txt" 读取失败
/* 方法二 */
// 列举的路径中,所有情况下都可以

 Linux服务机

/* 方法一 */
// path = "/config/hello.txt"  读取成功
// path = "config/hello.txt"   读取成功

// path = "./config/hello.txt" 读取失败
/* 方法二 */
// path = "classpath*:./config/hello.txt"  读取失败
// path = "classpath*:./config/*.txt"      读取失败

// path = classpath:./config/hello.txt     读取成功
// path = classpath:./config/*.txt         读取成功
// 其他均可
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AlgebraFly

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值