创建 Spring Boot Web应用程序时,有时需要从类路径加载文件,例如,数据仅作为文件提供。就我而言,我使用 MAXMIND GeoLite2 database 进行位置索引。因此我需要加载文件并创建一个存储在服务器内存中的DatabaseReader对象。
在下面您将找到在WAR和JAR中加载文件的解决方案。
The ResourceLoader
用java开发时,你可以用当前线程获取classloader,然后用它去加载文件,但是在spring框架中提供了你更优雅得解决方法,如 ResourceLoader
你只需要注入对象 ResourceLoader
之后调用它的 getResource("yourPath")
方法即可。
在Spring Boot(WAR)中从资源目录/ classpath加载文件的示例
在以下示例中,我们从类路径中加载一个名为GeoLite2-Country.mmdb的文件作为资源,然后将其作为File对象进行检索。
@Service("geolocationservice") public class GeoLocationServiceImplimplements GeoLocationService{ private static final Logger LOGGER = LoggerFactory.getLogger(GeoLocationServiceImpl.class); private static DatabaseReader reader = null; private ResourceLoader resourceLoader; @Autowired public GeoLocationServiceImpl(ResourceLoader resourceLoader){ this.resourceLoader = resourceLoader; } @PostConstruct public void init(){ try { LOGGER.info("GeoLocationServiceImpl: Trying to load GeoLite2-Country database..."); Resource resource = resourceLoader.getResource("classpath:GeoLite2-Country.mmdb"); File dbAsFile = resource.getFile(); // Initialize the reader reader = new DatabaseReader .Builder(dbAsFile) .fileMode(Reader.FileMode.MEMORY) .build(); LOGGER.info("GeoLocationServiceImpl: Database was loaded successfully."); } catch (IOException | NullPointerException e) { LOGGER.error("Database reader cound not be initialized. ", e); } } @PreDestroy public void preDestroy(){ if (reader != null) { try { reader.close(); } catch (IOException e) { LOGGER.error("Failed to close the reader."); } } } }
从Spring Boot JAR中加载文件
如果你期望在springboot jar中从classpath中加载文件,你必须要使用 resource.getInputStream()
方法用输入流去获取文件,如果你试图使用 resource.getFile()
方法,你将会遇到错误,因为这样的话,spring试图去访问(主机的)文件系统的路径,但是在JAR中是做不到的。
@Service("geolocationservice") public class GeoLocationServiceImplimplements GeoLocationService{ private static final Logger LOGGER = LoggerFactory.getLogger(GeoLocationServiceImpl.class); private static DatabaseReader reader = null; private ResourceLoader resourceLoader; @Inject public GeoLocationServiceImpl(ResourceLoader resourceLoader){ this.resourceLoader = resourceLoader; } @PostConstruct public void init(){ try { LOGGER.info("GeoLocationServiceImpl: Trying to load GeoLite2-Country database..."); Resource resource = resourceLoader.getResource("classpath:GeoLite2-Country.mmdb"); InputStream dbAsStream = resource.getInputStream(); // <-- this is the difference // Initialize the reader reader = new DatabaseReader .Builder(dbAsStream) .fileMode(Reader.FileMode.MEMORY) .build(); LOGGER.info("GeoLocationServiceImpl: Database was loaded successfully."); } catch (IOException | NullPointerException e) { LOGGER.error("Database reader cound not be initialized. ", e); } } @PreDestroy public void preDestroy(){ if (reader != null) { try { reader.close(); } catch (IOException e) { LOGGER.error("Failed to close the reader."); } } } }
原文链接
问题1说明:
用IntelijIEDA开发spring boot web服务时,项目有一个文件需要加载到项目中,因为项目没有构建成jar,在项目中使用 ResourceUtils.getFile("classpath:filepath")
时,项目没有问题,打包成jar部署时,遇到了
java.io.FileNotFoundException: class path resource [static/Sample.txt] cannot be resolved to absolute file path because it does not reside in the file system: jar:file:/C:/workspace-test/XXX/target/XXX-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/static/Sample.txt
这样的错误,百度查了一大堆没有实质性帮助的结果,后来去StackOverflow上查到了,这样回答的
I have checked your code.If you would like to load a file from classpath in a Spring Boot JAR, then you have to use the resource.getInputStream() rather than resource.getFile().If you try to use resource.getFile() you will receive an error, because Spring tries to access a file system path, but it can not access a path in your JAR
解决方法:
因为项目中用了第三方jar包(这里还有一个开发问题),接口需要文件路径字符串,jar中又不能getFile().getURL(),所以不能获取相对路径,网上有一个把文件读入inputStream中,然后再写到项目的路径下,保存问临时文件,在获取路径。尝试了,还是不行。最后是在jar中读取部署服务的主机上真实存在的资源文件,使用绝对路径(其实我是把jar中文件拷贝到主机的目录下,然后再写上绝对路径)。
问题2说明:
使用了第三方jar,用maven构建项目打包时,会找不到第三方jar资源,因此会报 ClassNotFound
错误
棘角方法
解决方法有很多,我才用把第三方jar发布到本地maven仓库中,但是这样做项目的协同开发不太友好,其他开发者电脑上的本地maven仓库也需要进行相同的jar发布才可以。考虑到我是一个人开发,就没有但有这么多。
注
翻译它是因为最近写一个web服务时遇到了点问题,这篇文章起了一点作用
谢谢你的打赏
支付宝
微信