Spring Boot打包Jar后如何读取resources/key目录下的资源?
Spring Boot Jar包中读取resources下密钥文件的正确方式
问题原因
项目打包成Jar后,resources目录下的资源不再是文件系统中的独立文件,而是嵌入在Jar包内部的条目。原代码依赖文件系统路径的Paths.get()和Files.readAllBytes()方法,自然会失效。
解决方案
方式一:原生Java API读取(通用所有Java项目)
通过ClassLoader.getResourceAsStream()获取资源输入流,再将流转换为字符串,完全适配Jar包环境:
修改你的密钥内容获取方法:
import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; private String getPublicKeyContent() throws IOException { // 从classpath根目录读取资源,路径无需带开头/ try (InputStream is = getClass().getClassLoader().getResourceAsStream("key/public.pem")) { if (is == null) { throw new IOException("公钥文件未找到:classpath:key/public.pem"); } // Java 9+ 可用InputStream.readAllBytes()直接读取 return new String(is.readAllBytes(), StandardCharsets.UTF_8); } } private String getPrivateKeyContent() throws IOException { try (InputStream is = getClass().getClassLoader().getResourceAsStream("key/private.pem")) { if (is == null) { throw new IOException("私钥文件未找到:classpath:key/private.pem"); } return new String(is.readAllBytes(), StandardCharsets.UTF_8); } }
如果使用Java 8及以下版本,需要手动逐行读取流:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; private String getPublicKeyContent() throws IOException { try (InputStream is = getClass().getClassLoader().getResourceAsStream("key/public.pem"); BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) { if (is == null) { throw new IOException("公钥文件未找到:classpath:key/public.pem"); } StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } return sb.toString(); } }
方式二:Spring Boot专属方式(适配Spring生态)
如果你的类是Spring管理的Bean,可以注入ResourceLoader来读取资源,更贴合Spring Boot的使用习惯:
import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Component; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.security.*; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.security.spec.X509EncodedKeySpec; import java.util.Base64; @Component public class KeyLoader { private final ResourceLoader resourceLoader; // 构造方法注入ResourceLoader public KeyLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } private String getPublicKeyContent() throws IOException { Resource resource = resourceLoader.getResource("classpath:key/public.pem"); if (!resource.exists()) { throw new IOException("公钥文件未找到:classpath:key/public.pem"); } return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8); } private String getPrivateKeyContent() throws IOException { Resource resource = resourceLoader.getResource("classpath:key/private.pem"); if (!resource.exists()) { throw new IOException("私钥文件未找到:classpath:key/private.pem"); } return new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8); } // 你原有的getPublicKey和getPrivateKey方法保持不变 public PublicKey getPublicKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { String key = getPublicKeyContent().replaceAll("\\n", "") .replace("-----BEGIN PUBLIC KEY-----", "").replace("-----END PUBLIC KEY-----", ""); X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(key)); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePublic(keySpec); } public PrivateKey getPrivateKey() throws NoSuchAlgorithmException, InvalidKeySpecException, IOException { String key = getPrivateKeyContent().replaceAll("\\n", "") .replace("-----BEGIN PRIVATE KEY-----", "").replace("-----END PRIVATE KEY-----", ""); PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(key)); KeyFactory kf = KeyFactory.getInstance("RSA"); return kf.generatePrivate(keySpec); } }
注意事项
- 资源路径不要以
/开头:getClassLoader().getResourceAsStream("key/private.pem")是正确写法,带/会导致资源找不到。 - 确保打包时资源被正确包含:Maven/Gradle默认会打包
src/main/resources下的所有内容,无需额外配置,除非你修改了资源目录规则。 - 必须关闭输入流:使用try-with-resources语法自动关闭流,避免资源泄漏。
内容的提问来源于stack exchange,提问作者Jack
相关产品推荐
相关产品推荐

