SpringBoot读取war包jar包Resource资源文件解决办法

在开发过程中我们经常会碰到要在代码中获取资源文件的情况,而我在最近在SpringBoot项目中时碰到一个问题,就是在本地运行时,获取本地的xml资源文件是能够获取到的,但是项目打成war包jar包启动运行时,就会发生问题,报找不到资源文件的错误。然后经过寻找排查确定了是下面代码通过ClassLoader获取路径的时候出错了。

 

常用方式:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/**

 * @author mazhq

 * @Title: TestMain

 * @ProjectName: zeus

 * @Description: TODO

 * @date 2019/3/5 16:10

 */

public class TestMain {

    public static void main(String[] args) {

        String path = TestMain.class.getClassLoader().getResource("1.xml").getPath();

        System.out.println(path);

    }

 /**

     * 输出:

     *

     */D:/demo_projects/sc-architecture/service-hi/target/classes/1.xml

     */

}

  

但是在将SpringBoot打包放到Linux服务器启动打印的目录为

1

/data/zeus/service-hi-1.0.0-SNAPSHOT.war!/WEB-INF/classes!/1.xml

可以看到在Linux中无法直接访问未经解压的文件,所以就会找不到文件。

解决办法

    最佳方法:ResourceUtils.getFile(ResourceUtils.CLASSPATH_URL_PREFIX+"1.xml").getPath();

        多模块项目需要把文件放在打war包模块resources下    classpath:1.xml

 

  1. 通过ClassLoadergetResourceAsStream()方法获取其流,就能够获取到。

  读取jar里面的文件,我们只能用流去读取,不能用File

1

2

3

4

5

6

7

8

9

public class TestMain {

    public static void main(String[] args) {

        try {

            List<String> content = IOUtils.readLines(TestMain.class.getClassLoader().getResourceAsStream("1.xml"), "UTF-8");

        catch (IOException e) {

            e.printStackTrace();

        }

    }

}

  2. 采用绝对路径将文件放到服务器某个路径,在application.properties中配置路径读取。

  3. 不推荐:将内容放到数据库中。

获取资源的两种方式

通常在开发过程中会碰到读取配置文件的问题,一般有两种方式进行读取。一种是Class.getResource(String path),一种是ClassLoader.getResource(String path),这两种虽然都能读取文件,但是在path的填写上有一点点的不同。

Class.getResource

  1. path以/开头:则是从ClassPath根下获取
  2. path不以/开头:默认是从此类所在的包下取资源

下面有个例子

1

2

3

4

5

6

7

8

9

10

11

12

public class TestMain {

    public static void main(String[] args) {

        System.out.println(TestMain.class.getResource("/"));

        System.out.println(TestMain.class.getResource(""));

    }

    /**

     * 输出:

     *

     * file:/D:/demo_projects/sc-architecture/service-hi/target/classes/

     * file:/D:/demo_projects/sc-architecture/service-hi/target/classes/com/mazhq/servicehi/

     */

}

  

那么读取在resource下的1.xml,就如下的获取方法
 

1

2

3

4

5

6

7

8

9

10

11

12

public class TestMain {

    public static void main(String[] args) {

        System.out.println(TestMain.class.getResource("/1.xml"));

        System.out.println(TestMain.class.getResource("../../../1.xml"));

    }

    /**

     * 输出:

     *

     * file:/D:/demo_projects/sc-architecture/service-hi/target/classes/1.xml

     * file:/D:/demo_projects/sc-architecture/service-hi/target/classes/1.xml

     */

}

ClassLoader.getResource

ClassLoader.getResource的path中不能以/开头,path是默认是从根目录下进行读取的

代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

public class TestMain {

    public static void main(String[] args) {

        System.out.println(TestMain.class.getClassLoader().getResource(""));

        System.out.println(TestMain.class.getClassLoader().getResource("/"));

    }

    /**

     * 输出:

     *

     * file:/D:/demo_projects/sc-architecture/service-hi/target/classes/

     * null

     */

}

  

从上面例子我们可以看到

TestMain.class.getClassLoader().getResource("")=TestMain.class.getResource("/")

 

两个获取资源文件的差别

其实查看Class.getResource中可以看到

1

2

3

4

5

6

7

8

9

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);

    }

他最后调用的还是ClassLoader.getResource这个方法,那么为什么会有path的差别呢,因为其resolveName方法中对传的/进行了解析,解析为了空字符串。

resolveName 方法实现如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

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;

   }<br><br>  //传入 "/" 返回  ""

  

最后:大家用的时候注意一下这些问题,避免在这个上面耽误时间。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值