1. 絮絮叨叨
- 自从梳理清楚了与Java文件有关的输入/输出流,现在读取或写入文件基本都不需要上网查资料了,简直磨刀不误砍柴工~ 🐯
- 感兴趣的,可以查看本人之前的博客:基于文件的java输入/输出流
2. 本地执行Java代码以读取文件
-
现在有个需求:读取countries文件,计算世界上有多少个国家
-
创建maven项目,编写代码、本地运行,那是溜溜的,几下就完成了
// 其他import省略,只展示sl4j的 import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class CountriesTest { private static final Logger logger = LoggerFactory.getLogger(CountriesTest.class); public static void main(String[] args) { try { CountriesTest test = new CountriesTest(); test.countCountries1(); } catch (IOException e) { logger.error("读取文件失败", e); } } /** * 直接读取指定目录的文件 **/ public void countCountries1() throws IOException { // 获取文件目录 String filePath = this.getClass().getResource("/").getPath().replace("/target/classes/", ""); logger.info("开始从指定目录读取文件, {} ...", filePath); try (BufferedReader reader = new BufferedReader(new FileReader(filePath + "/countries"))) { int count = 0; String country = reader.readLine(); while (country != null) { count++; // 获取下一个国家 country = reader.readLine(); } logger.info("There are {} countries in the world", count); } } }
-
其中,
slf4j
的maven依赖为<dependencies> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.30</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency>
-
countries文件,放在maven模块的父目录下:
-
countries文件的内容,是从网页上粘贴的,部分内容如下:
Afghanistan Albania Algeria Andorra
3. 以jar包的形式执行程序
3.1 打包后执行失败
-
现在需求升级:将代码打包成jar包,放到服务器上执行
-
这也很简单,一个打包命令
mvn clean package
就搞定 -
结果到服务器上执行时,却报错了。
-
通过对比代码,发现如下代码存在空指针问题
String filePath = this.getClass().getResource("/").getPath().replace("/target/classes/", "");
-
自己解压缩对应jar包,也没有在jar包里面找到countries文件。所以,即使文件目录正确,文件不存在,也会读取失败 😓
3.2 将文件放到resources目录
-
灵机一动,想到Java项目中还有个
src/main/resources
目录, -
Java Web项目中很多配置文件都是放在这里面的,打包时,好像也没有做什么额外的配置,最后还能读取这些文件
-
抱着试一试的心理,将countries文件移动到了resources目录
-
然后参考博客:java项目中获得resources目录下的文件或图片,直接以文件流的形式读取resources目录下的countries文件
-
最终完整的代码如下:
public class CountriesTest { private static final Logger logger = LoggerFactory.getLogger(CountriesTest.class); public static void main(String[] args) { try { CountriesTest test = new CountriesTest(); test.countCountries2(); } catch (IOException e) { logger.error("读取文件失败", e); } } /** * 从resources目录读取文件 **/ public void countCountries2() throws IOException { logger.info("开始从resources目录读取文件 ..."); // 获取resources下的文件资源 InputStream in = getClass().getResourceAsStream("/countries"); try (BufferedReader reader = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))) { int count = 0; String country = reader.readLine(); while (country != null) { count++; // 获取下一个国家 country = reader.readLine(); } logger.info("There are {} countries in the world", count); } catch (IOException e) { throw e; } } }
-
重新打包后,成功读取文件 👍
-
同时,jar包的根目录下出现了countries文件 —— 说明countries文件被成功打包
4. maven resources plugin
4.1 考虑将文件放到其他地方
-
将文件放在resources目录可以自动打包,如果放到其他层级呢?
-
下面是笔者的一些试验:
目录 是否ok 是否打包 src/main/java
目录failed No src/main/resources/files
目录ok,需要将文件路径更新为 InputStream in = getClass().getResourceAsStream("/files/countries");
Yes,在 files/
目录下src/files
目录failed No
4.2 指定resources目录
-
通过网上查阅资料,发现maven resources plugin,可以指定resources目录
-
按照官网描述,默认的resources目录为
src/main/resources
,可以通过如下方式在<build>
标签中指定resources目录<resources> <resource> <directory>resource1</directory> </resource> <resource> <directory>resource2</directory> </resource> <resource> <directory>resource3</directory> </resource> </resources>
-
以将countries文件放到
src/main/java
目录为例,配置如下:<resources> <resource> <directory>src/main/java</directory> </resource> <!-- 原本的默认值将被覆盖,需要显式指定--> <resource> <directory>src/main/resources</directory> </resource> </resources>
4.3 其他一些高级配置
4.3.1 指定或排除文件
-
在resources目录下,有各种类型的文件,只有某些文件在文件运行时是需要的
-
这时,可以通过
<include>
或<exclude>
指定或排除文件,详情参考官网:Including and excluding files and directories<resources> <resource> <directory>src/main/java</directory> <!-- 指定打包的文件类型,**/表示当前目录或任意子目录--> <includes> <include>**/*.txt</include> ... </includes> </resource> <!-- 原本的默认值将被覆盖,需要显式指定 --> <resource> <directory>src/main/resources</directory> <!-- 排除各种格式的图片文件 --> <excludes> <exclude>**/*.bmp</exclude> ... </excludes> </resource> </resources>
4.3.2 更新文件中的值
-
文件中,某些变量需要在编译时更新。如hello.txt中,包含
${name}
Hello ${name}
-
可以通过开启
filtering
属性,将${name}
更新为实际值<resource> <directory>src/main/resources</directory> <!-- 开启filter --> <filtering>true</filtering> </resource>
-
而
${name}
的来源主要有三种,具体参考官网:Filtering- maven自带的属性,如
${basedir}
、${name}
,可以参考作者之前的博客:10. maven属性,或者网上查阅资料 - 执行maven命令是,通过
-Dxxx
传入的值,类似mvn resources:resources -Dname="world"
- 自定义的property,可以放在pom文件中,也可以使用单独的
.properties
文件存储
- maven自带的属性,如