springboot读取绝对路径如何处理

本文讲述了如何在SpringBoot应用中集成Kafka时,处理证书文件路径问题,从使用`getClass().getClassLoader().getResource()`的局限性,到利用Spring的ResourceLoader接口和Kubernetes中的Volume挂载、环境变量传递,以适应容器化环境的需求。
摘要由CSDN通过智能技术生成

我的需求是springboot集成kafka需要读取证书文件,然后进行消费工厂的创建和监听,当然这里需要用到绝对路径文件的读取,相对路径是不支持的。

开始之前我们使用常用的几种方式获取路径文件的方式:

getClass().getClassLoader().getResource() 方式

@Bean
    public DefaultKafkaConsumerFactory <String, String> consumerFactory() {
        Map <String, Object> consumerConfig = new HashMap <>();
        consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
        consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "owner-data-sync");
        consumerConfig.put(ConsumerConfig.CLIENT_ID_CONFIG, "owner-data-sync");

        consumerConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        consumerConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

        // 设置值的反序列化器为 ErrorHandlingDeserializer2,并配置类型信息
        consumerConfig.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, JsonDeserializer.class);
        consumerConfig.put(JsonDeserializer.USE_TYPE_INFO_HEADERS, false); // 启用类型信息头
        consumerConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");



        String pemUrl = "";
        String csrUrl = "";
        if (c3ConfigProperties.getEnvironment().equals("dev")) {
            pemUrl = "file/dev/kafka/client.jks";
            csrUrl = "file/dev/kafka/truststore.jks";
        } else if (c3ConfigProperties.getEnvironment().equals("beta")) {
            pemUrl = "file/beta/kafka/client.jks";
            csrUrl = "file/beta/kafka/truststore.jks";

        } else if (c3ConfigProperties.getEnvironment().equals("prod")) {
            pemUrl = "file/prod/kafka/client.jks";
            csrUrl = "file/prod/kafka/truststore.jks";
        }
        URL resourceUrl = getClass().getClassLoader().getResource(pemUrl);
        URL csrResourceUrl = getClass().getClassLoader().getResource(csrUrl);
        String keyStorePath = new File(resourceUrl.getFile()).getAbsolutePath();
        String trustStorePath = new File(csrResourceUrl.getFile()).getAbsolutePath();

        consumerConfig.put("security.protocol", "SSL");
        consumerConfig.put("ssl.truststore.password", kafkaProperties.getTrustStorePassword());
        consumerConfig.put("ssl.keystore.location", keyStorePath);
        consumerConfig.put("ssl.truststore.location", trustStorePath);
        consumerConfig.put("ssl.keystore.password", kafkaProperties.getKeyStorePassword());
        consumerConfig.put("ssl.key.password", kafkaProperties.getKeyPassword());

        return new DefaultKafkaConsumerFactory <>(consumerConfig);
    }

这段代码看着没有问题实际上有很大问题,getClass().getClassLoader().getResource() 方法无法直接读取文件系统中的绝对路径,它是基于类路径的。这意味着它只能读取位于类路径下的资源文件,而无法直接访问文件系统中的其他位置的文件,所以在容器pod下的java服务其实这样运行必然报错的。

Resource 方式:

  1. 更灵活的资源管理:Spring 的 ResourceLoader 接口提供了更灵活的资源管理功能,可以处理各种类型的资源,包括类路径下的文件、URL、文件系统中的文件等。

  2. 依赖注入支持:在 Spring 应用程序中,ResourceLoader 可以通过依赖注入的方式使用,这使得在 Spring 环境中管理资源文件变得更加方便。

  3. 资源抽象:Spring 的 Resource 接口提供了对资源的抽象表示,使得可以统一处理各种类型的资源,提供了更高层次的抽象和功能。

  4. 丰富的功能:Spring 的 ResourceLoader 接口提供了丰富的功能,如资源文件的装饰、转换等操作,使得资源文件的读取和处理更加灵活和方便。

缺点:相对于直接使用 getClass().getClassLoader().getResource() 方法,使用 Spring 的 ResourceLoader 接口可能会带来一定的性能开销,因为它涉及到更多的抽象和功能 

我们基于代码上改了一下:

    @Bean
    public DefaultKafkaConsumerFactory <String, String> consumerFactory() {
        Map <String, Object> consumerConfig = new HashMap <>();
        consumerConfig.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, kafkaProperties.getBootstrapServers());
        consumerConfig.put(ConsumerConfig.GROUP_ID_CONFIG, "owner-data-sync");
        consumerConfig.put(ConsumerConfig.CLIENT_ID_CONFIG, "owner-data-sync");

        consumerConfig.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
        consumerConfig.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);

        // 设置值的反序列化器为 ErrorHandlingDeserializer2,并配置类型信息
        consumerConfig.put(ErrorHandlingDeserializer.VALUE_DESERIALIZER_CLASS, JsonDeserializer.class);
        consumerConfig.put(JsonDeserializer.USE_TYPE_INFO_HEADERS, false); // 启用类型信息头
        consumerConfig.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");


        String pemUrl = "";
        String csrUrl = "";
        if (c3ConfigProperties.getEnvironment().equals("dev")) {
            pemUrl = "file/dev/kafka/client.jks";
            csrUrl = "file/dev/kafka/truststore.jks";
        } else if (c3ConfigProperties.getEnvironment().equals("beta")) {
            pemUrl = "file/beta/kafka/client.jks";
            csrUrl = "file/beta/kafka/truststore.jks";

        } else if (c3ConfigProperties.getEnvironment().equals("prod")) {
            pemUrl = "file/prod/kafka/client.jks";
            csrUrl = "file/prod/kafka/truststore.jks";
        }

       try {

           // 获取证书资源
           Resource pemResource = resourceLoader.getResource("classpath:"+pemUrl);
           Resource csrResource = resourceLoader.getResource("classpath:"+csrUrl);
// 获取证书文件的路径
           String keyStorePath = pemResource.getFile().getAbsolutePath();
           String trustStorePath = csrResource.getFile().getAbsolutePath();
           consumerConfig.put("ssl.keystore.location", keyStorePath);
           consumerConfig.put("ssl.truststore.location", trustStorePath);

       }catch (Exception e){

       }
        consumerConfig.put("security.protocol", "SSL");
        consumerConfig.put("ssl.truststore.password", kafkaProperties.getTrustStorePassword());
        consumerConfig.put("ssl.keystore.password", kafkaProperties.getKeyStorePassword());
        consumerConfig.put("ssl.key.password", kafkaProperties.getKeyPassword());

        return new DefaultKafkaConsumerFactory <>(consumerConfig);
    }

 这是方式直接读取resource目录下面的文件也是不行,只能配合挂载的方式进行读取。

volumes:
  - name: keystore-volume
    hostPath:
      path: /path/to/keystore.p12

springboot的yaml 方式读取文件:

spring:
  kafka:
    bootstrap-servers: SSL://*-*:443
    ssl:
      protocol: TLS
      trust-store-password: a123456
      key-store-password: a123456
      key-password: a123456
      key-store-location: file:client.jks
      trust-store-location: file:truststore.jks
    consumer:
      value-deserializer: org.apache.kafka.common.serialization.ByteArrayDeserializer
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      properties:
        security.protocol: SSL
        auto.offset.reset: earliest
        client.id: owner-data-sync
        max.poll.records: 100 # 每次拉取的最大记录数
      group-id: owner-data-sync
    listener:
      concurrency: 3  
    producer:
      topic: *.event

在容器化环境中,尤其是在 Kubernetes 中,直接使用 file: 方式指定文件路径可能会遇到一些问题,因为容器中的文件系统通常是短暂的,并且不同于本地文件系统。

  • 容器化环境的限制:在容器中直接使用 file: 方式指定文件路径可能会受到容器化环境的限制,因为容器中的文件系统通常是隔离的,并且不支持直接访问宿主机的文件系统。

  • 使用 Volume 挂载文件:在 Kubernetes 中,可以通过 Volume 将宿主机的文件挂载到容器中,然后在应用程序中访问挂载的路径来读取文件。可以在 Kubernetes 的 Pod 配置中通过 Volume 将文件挂载到容器中,并在 Spring Boot 配置文件中使用 file: 方式指定挂载路径。

  • 环境变量传递文件路径:另一种方法是通过环境变量将文件路径传递给应用程序,然后在应用程序中读取该环境变量并使用对应的路径来访问文件。

如果需要使用这样的方式一定要把文件上传到指定的目录

 

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
在Spring Boot中,可以使用`ResourceLoader`来加载根目录下的文件。以下是一些常见的方法: 1. 通过`ResourceLoader`加载文件 ```java import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.nio.file.Files; import java.nio.file.Paths; @Component public class MyComponent { private final ResourceLoader resourceLoader; public MyComponent(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } @PostConstruct public void init() throws Exception { Resource resource = resourceLoader.getResource("classpath:myfile.txt"); byte[] contents = Files.readAllBytes(Paths.get(resource.getURI())); System.out.println(new String(contents)); } } ``` 2. 通过`ClassPathResource`加载文件 ```java import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.nio.file.Files; import java.nio.file.Paths; @Component public class MyComponent { @PostConstruct public void init() throws Exception { ClassPathResource resource = new ClassPathResource("myfile.txt"); byte[] contents = Files.readAllBytes(Paths.get(resource.getURI())); System.out.println(new String(contents)); } } ``` 以上代码中,第一个例子是通过`ResourceLoader`加载文件,第二个例子是通过`ClassPathResource`加载文件。注意,文件路径中的`classpath:`前缀表示文件在类路径下。如果文件不在类路径下,可以使用`file:`前缀来指定绝对路径

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小杨互联网

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

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

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

打赏作者

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

抵扣说明:

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

余额充值