Spring Boot读取resources下PEM文件在服务器/Docker中报错的解决方案
Spring Boot读取resources下PEM文件部署后空指针问题解决
问题现象
本地启动Spring Boot应用时,读取src/main/resources目录下的marchent_private.pem文件正常,但将jar包部署到VPS服务器或通过Docker运行时,抛出如下异常:
java.lang.NullPointerException: Cannot invoke "String.getBytes(java.nio.charset.Charset)" because "src" is null
用户使用的读取代码如下:
public PrivateKey getPrivate() throws Exception { ClassPathResource resource = new ClassPathResource("marchent_private.pem"); InputStream inputStream = resource.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); String content = reader.readLine(); System.out.println(content); content = content.replaceAll("\\s", ""); byte[] keyBytes = Base64.getDecoder().decode(content); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(spec); }
问题原因
- PEM文件读取不完整:PEM格式私钥包含多行内容(含头尾标记行),代码仅用
readLine()读取第一行,部署环境中可能因编码、换行格式差异导致第一行读取为空,触发空指针。 - 流未正确关闭:代码未处理流的关闭操作,可能引发资源泄漏或读取异常。
- 资源打包问题:jar包构建时可能未将
marchent_private.pem正确打包进去,导致运行时无法找到文件。
解决方案
1. 修正PEM文件读取逻辑
改为读取所有行,过滤头尾标记并拼接有效内容,同时使用try-with-resources自动关闭流:
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PrivateKey; import java.security.spec.PKCS8EncodedKeySpec; import java.util.Base64; import org.springframework.core.io.ClassPathResource; public PrivateKey getPrivate() throws Exception { ClassPathResource resource = new ClassPathResource("marchent_private.pem"); // try-with-resources自动关闭流,避免资源泄漏 try (InputStream inputStream = resource.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { StringBuilder keyContent = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { // 跳过私钥的头尾标记行 if (!line.startsWith("-----BEGIN PRIVATE KEY-----") && !line.startsWith("-----END PRIVATE KEY-----")) { keyContent.append(line.trim()); } } String encodedKey = keyContent.toString(); if (encodedKey.isEmpty()) { throw new IllegalArgumentException("私钥文件内容无效或为空"); } byte[] keyBytes = Base64.getDecoder().decode(encodedKey); PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(spec); } }
2. 验证资源文件是否正确打包
- 执行命令检查jar包内是否包含目标文件:
若输出为空,说明文件未被打包,需检查构建工具配置:jar tf your-app.jar | grep marchent_private.pem- Maven项目确保
pom.xml中没有排除src/main/resources的配置,默认Maven会自动打包该目录下的文件。 - Gradle项目确保
build.gradle中包含sourceSets.main.resources.srcDirs = ['src/main/resources']配置。
- Maven项目确保
3. Docker部署检查
- 确保Dockerfile正确复制了包含资源文件的jar包:
FROM openjdk:17-jdk-slim WORKDIR /app COPY target/your-app.jar ./app.jar CMD ["java", "-jar", "app.jar"] - 构建镜像前,先执行
mvn clean package(Maven)或gradle build(Gradle)生成最新的jar包,避免使用旧包导致资源缺失。
内容的提问来源于stack exchange,提问作者Rumaion Tomal
相关产品推荐
相关产品推荐

