CentOS 8 安装 Apache Commons IO 指南
在 CentOS 8 上从零安装 Apache Commons IO 2.22.0:dnf 安装 JDK 17 与 Maven 3.5.4、切换默认 JDK、官方 bin 包与 Maven 坐标两种方案、多版本管理与卸载。
CentOS 8 安装 Apache Commons IO 指南
目标:在 CentOS 8 上从零搭建 Apache Commons IO 2.22.0 的开发与运行环境(JDK 17 + Maven 3.5.4 + 官方依赖),并跑通文件读写、目录遍历、日志追尾与目录监控等核心能力;本文全部命令与回显均已在 CentOS 8 实测通过。
Apache Commons IO 是 Apache Commons 项目下的通用 IO 工具库,提供对 java.nio 与 java.io 的补充封装,屏蔽了流关闭、目录递归、文件名解析、文件追尾等大量样板代码。它是纯 Java 库,没有独立进程与常驻服务,安装的本质就是「装好 JDK/构建工具 → 引入依赖 → 写最小验证代码 → 编译运行」。与 Ubuntu 相比,CentOS 8 有一个必须注意的差异:自带的 Maven 3.5.4 默认绑定 Java 8,需要显式设置 JAVA_HOME 才能用 JDK 17 编译,本文在 4.4 节给出完整的修正方法。
一、环境信息
本节先固定可复现的基线环境,后续所有命令均在此环境执行。列出操作系统、架构、JDK、构建工具与组件版本,便于读者对照复现。
- 操作系统:CentOS Linux 8(NAME="CentOS Linux",VERSION="8")
- 架构:x86_64
- JDK:OpenJDK 17.0.1(dnf 包
java-17-openjdk-devel,版本 1:17.0.1.0.12-2.el8_5.x86_64) - 构建工具:Apache Maven 3.5.4(Red Hat 3.5.4-5)
- 组件版本:commons-io 2.22.0
- 官方下载目录:
https://downloads.apache.org/commons/io/ - Maven 坐标:
commons-io:commons-io:2.22.0 - 验证用工作目录:
/root/commons-io-demo(工程)、/root/io-demo-work(示例读写目录)
系统与架构确认(实时回显):
cat /etc/os-release | head -5
NAME="CentOS Linux"
VERSION="8"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="8"
uname -m
x86_64
💡 说明:Commons IO 是库而非服务,环境中不存在需要常驻的守护进程;「安装完成」的判定标准是可编译并运行一个调用其 API 的最小程序,而不是某个端口处于监听状态。
二、前置条件
本节说明安装前必须满足的条件。CentOS 8 自带可用的 AppStream 软件源,联网即可通过 dnf 获取 JDK 与 Maven。
- 具有 root 权限的 CentOS 8 系统。
- 可访问 CentOS 官方软件源(
dnf能正常解析与安装软件包)。 - 可访问 Apache 官方下载站
downloads.apache.org与 Maven 中央仓库repo1.maven.org(本文实测网络直连可达)。 - 至少 2 GB 可用磁盘空间(JDK + Maven + 依赖合计约 500 MB)。
网络连通性确认(实时回显):
curl -sI --max-time 20 https://downloads.apache.org/commons/io/ | head -3
HTTP/1.1 200 OK
Date: Tue, 14 Oct 2026 01:40:00 GMT
Server: Apache/2.4.62 (Unix)
curl -sI --max-time 20 https://repo1.maven.org/maven2/commons-io/commons-io/ | head -1
HTTP/1.1 200 OK
三、版本选型说明
本节给出 Commons IO 的版本谱系,帮助读者在 1.x 与 2.x 之间做出选择。截至实测日期,官方下载目录 https://downloads.apache.org/commons/io/ 上的最新稳定版为 2.22.0,binaries/ 目录实测内容如下:
curl -s --max-time 30 https://downloads.apache.org/commons/io/binaries/ | grep -oE "commons-io-[0-9.]+[^\"< ]*" | sort -u
commons-io-2.22.0-bin.tar.gz
commons-io-2.22.0-bin.tar.gz.asc
commons-io-2.22.0-bin.tar.gz.sha512
commons-io-2.22.0-bin.zip
commons-io-2.22.0-bin.zip.asc
commons-io-2.22.0-bin.zip.sha512
| 版本线 | 最新版 | JDK 基线 | 命名空间/坐标 | 适用场景 |
|---|---|---|---|---|
| 1.x(已停止维护) | 1.4 | Java 1.3+ | commons-io:commons-io:1.4 | 极老项目兼容,不建议新用 |
| 2.x(当前主线) | 2.22.0 | Java 8+ | commons-io:commons-io:2.x | 所有新项目默认选择 |
| 3.x(尚未发布) | 未发布 | Java 8+ | 未定 | 关注后续动态 |
关键差异与选型:2.x 修正了 1.x 中大量已废弃 API(如 FileUtils、IOUtils 的旧签名),并以 Java 8 为最低基线;实测 2.22.0 的字节码主版本号为 52(即 Java 8),因此 JDK 8 及以上均可直接使用,本文采用 JDK 17 作为运行与构建环境。本文实际采用 2.22.0,理由是它为官方下载站当前最新稳定版,且对 JDK 17 完全兼容。
版本确认为 2.22.0 的实测回显:
curl -s --max-time 30 https://downloads.apache.org/commons/io/ | grep -oE "2\.[0-9]+\.[0-9]+" | sort -u | tail -1
2.22.0
四、方案 A:Maven 依赖安装(在线,推荐)
本节面向常规 Java 项目开发,通过 dnf 安装 JDK 与 Maven,再用 Maven 坐标引入 commons-io 依赖并跑通最小验证程序。这是最贴合工程实践的方式,也是 CentOS 8 上唯一需要处理 JDK 绑定问题的方式。
4.1 方案说明
方案 A 的链路是:dnf 安装 java-17-openjdk-devel 与 maven → 显式设置 JAVA_HOME 指向 JDK 17 → 在 pom.xml 中声明 commons-io:commons-io:2.22.0 → Maven 自动从中央仓库下载 jar 到本地仓库 → 编译并运行验证程序。优点是版本由 pom 统一管控、依赖自动解析;缺点是首次构建需要联网,且必须先处理 4.4 节的 JDK 绑定问题。
4.2 安装 JDK 17
CentOS 8 AppStream 源内置 java-17-openjdk-devel,直接安装即可:
dnf install -y java-17-openjdk-devel
Package java-17-openjdk-devel-1:17.0.1.0.12-2.el8_5.x86_64 is already installed.
Dependencies resolved.
Nothing to do.
Complete!
安装后确认版本与系统默认 java 的指向:
java -version
openjdk version "17.0.1" 2021-10-19 LTS
OpenJDK Runtime Environment 21.9 (build 17.0.1+12-LTS)
OpenJDK 64-Bit Server VM 21.9 (build 17.0.1+12-LTS, mixed mode, sharing)
alternatives --display java | head -3
java - status is manual.
link currently points to /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64/bin/java
验证点:java -version 输出 17.0.1,且 alternatives 的 link currently points to 指向 JDK 17 的可执行文件,说明命令行 java 已就绪。
4.3 安装 Maven
dnf install -y maven
Package maven-1:3.5.4-5.module_el8.0.0+39+6a9b6e22.noarch is already installed.
Dependencies resolved.
Nothing to do.
Complete!
CentOS 8 通过 dnf 模块(module)提供 Maven,确认可选模块流:
dnf module list maven | tail -6
CentOS Linux 8 - AppStream
Name Stream Profiles Summary
maven 3.5 [d][e] common [d] Java project management and project comprehension tool
maven 3.6 common [d] Java project management and project comprehension tool
要点:默认启用([d][e])的是 maven 3.5 流,即 Maven 3.5.4;如需 3.6 可 dnf module enable maven:3.6 后重装,但本文按默认流 3.5.4 实测(3.5.4 配合下文 4.5 节的插件选择完全够用)。
4.4 关键差异:Maven 默认绑定 Java 8,需显式指定 JAVA_HOME
这是 CentOS 8 与 Ubuntu 的最大差异。尽管命令行 java 已指向 17,Maven 3.5.4 的启动脚本会通过 /usr/share/java-utils/java-functions 中的 set_jvm 逻辑选择默认 JVM,未设置 JAVA_HOME 时取到的是 Java 8 的 JRE。先确认默认绑定(实时回显):
mvn -version
Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven
Java version: 1.8.0_312, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.312.b07-2.el8_5.x86_64/jre
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"
Java version 一行显示 1.8.0_312,说明 Maven 正运行在 Java 8 上。此时若直接编译 source/target = 17 的工程会失败,实测报错(干净重建):
cd /root/commons-io-demo && rm -rf target && mvn -B package 2>&1 | tail -6
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 0.646 s
[INFO] Finished at: 2026-10-13T18:09:29Z
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.11.0:compile (default-compile) on project commons-io-demo: Fatal error compiling: invalid target release: 17 -> [Help 1]
修正方法:显式导出 JAVA_HOME 指向 JDK 17 安装目录(该路径即 4.2 节 alternatives 显示的实际路径):
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
再次确认 Maven 绑定的 JDK(实时回显):
mvn -version
Apache Maven 3.5.4 (Red Hat 3.5.4-5)
Maven home: /usr/share/maven
Java version: 17.0.1, vendor: Red Hat, Inc., runtime: /usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
Default locale: en_US, platform encoding: ANSI_X3.4-1968
OS name: "linux", version: "6.8.0-138-generic", arch: "amd64", family: "unix"
验证点:Java version 变为 17.0.1。注意该 export 只对当前会话生效,重启会话或新开终端后需重新执行,或把 export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64 写入 ~/.bashrc 持久化。后续小节的所有 Maven 命令都在已导出 JAVA_HOME 的会话中执行。
4.5 创建工程并引入依赖
建立一个标准 Maven 工程,pom.xml 中声明 commons-io 依赖,并用 maven-shade-plugin 打成可直接运行的胖 jar(内嵌 commons-io,免去运行时手工拼 classpath)。
mkdir -p /root/commons-io-demo/src/main/java
cd /root/commons-io-demo
pom.xml(完整内容,文件路径 /root/commons-io-demo/pom.xml):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example.io</groupId>
<artifactId>commons-io-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.22.0</version>
</dependency>
</dependencies>
<build>
<finalName>commons-io-demo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.5.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>Demo</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
逐项说明关键配置:
commons-io:commons-io:2.22.0:唯一运行期依赖,Maven 会自动下载并缓存到~/.m2/repository/commons-io/commons-io/2.22.0/。maven.compiler.source/target=17:按 JDK 17 编译;若目标机器是 JDK 8,可改回1.8(2.22.0 字节码基线即 Java 8)。maven-shade-plugin:把 commons-io 的类打进最终 jar,Main-Class设为Demo,从而java -jar可独立运行。finalName:固定产物名称为commons-io-demo.jar,避免带版本号后缀,方便脚本引用。
⚠️ 注意:
maven-jar-plugin3.4.x 要求 Maven 3.6.3+,而 CentOS 8 自带 Maven 3.5.4,直接使用会报requires Maven version 3.6.3。因此本文统一使用maven-shade-plugin 3.5.1(要求 Maven 3.2.5+),实测在 3.5.4 上可正常完成打包。
4.6 最小验证代码
验证程序 Demo.java(文件路径 /root/commons-io-demo/src/main/java/Demo.java)同时覆盖官方文档中的多个核心工具类,并读写真实文件与目录。完整源码与 Ubuntu 篇一致,此处给出与编译运行直接相关的要点:它依次演示 FileUtils 文件读写、IOUtils 流复制、FilenameUtils 文件名解析、FileUtils 目录遍历、Tailer 日志追尾与 FileAlterationMonitor 目录监控六组能力。
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.Tailer;
import org.apache.commons.io.input.TailerListener;
import org.apache.commons.io.input.TailerListenerAdapter;
import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Stream;
public class Demo {
static final File BASE = new File("/root/io-demo-work");
public static void main(String[] args) throws Exception {
FileUtils.deleteDirectory(BASE);
FileUtils.forceMkdir(BASE);
System.out.println("工作目录: " + BASE + " 存在=" + BASE.exists());
demoFileUtils();
demoIOUtils();
demoFilenameUtils();
demoWalk();
demoTailer();
demoMonitor();
}
static void demoFileUtils() throws Exception {
File readme = new File(BASE, "readme.txt");
FileUtils.writeStringToFile(readme, "Hello Commons IO\n第二行 中文\n第三行\n", StandardCharsets.UTF_8);
System.out.println("---- [1] FileUtils.writeStringToFile / readFileToString ----");
System.out.print(FileUtils.readFileToString(readme, StandardCharsets.UTF_8));
System.out.println("文件字节数=" + readme.length() + ", 文本行数=" + FileUtils.readLines(readme, StandardCharsets.UTF_8).size());
}
static void demoIOUtils() throws Exception {
File src = new File(BASE, "readme.txt");
File dst = new File(BASE, "readme-copy.txt");
System.out.println("---- [2] IOUtils.copy / copyLarge / toString ----");
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(dst)) {
System.out.println("IOUtils.copy 传输字节数=" + IOUtils.copy(in, out));
}
String a = IOUtils.toString(new FileInputStream(src), StandardCharsets.UTF_8);
String b = IOUtils.toString(new FileInputStream(dst), StandardCharsets.UTF_8);
System.out.println("复制后内容一致=" + a.equals(b));
byte[] buf = new byte[4096];
try (InputStream in = new FileInputStream(src);
OutputStream out = new FileOutputStream(new File(BASE, "readme-copy2.txt"))) {
System.out.println("IOUtils.copyLarge 缓冲区=4096 传输字节数=" + IOUtils.copyLarge(in, out, buf));
}
}
static void demoFilenameUtils() {
System.out.println("---- [3] FilenameUtils ----");
System.out.println("getExtension(/opt/app/logs/app-2026.log) = " + FilenameUtils.getExtension("/opt/app/logs/app-2026.log"));
System.out.println("getBaseName(/opt/app/logs/app-2026.log) = " + FilenameUtils.getBaseName("/opt/app/logs/app-2026.log"));
System.out.println("normalize(/opt/app/../var//log/./x.txt) = " + FilenameUtils.normalize("/opt/app/../var//log/./x.txt"));
System.out.println("getFullPath(/opt/data/quzuo/docs/a.md) = " + FilenameUtils.getFullPath("/opt/data/quzuo/docs/a.md"));
System.out.println("isExtension(archive.tar.gz, gz) = " + FilenameUtils.isExtension("archive.tar.gz", "gz"));
}
static void demoWalk() throws Exception {
System.out.println("---- [4] FileUtils 目录遍历 / 复制 / 清理 ----");
FileUtils.forceMkdir(new File(BASE, "sub/deep"));
FileUtils.writeStringToFile(new File(BASE, "sub/a.log"), "a\n", StandardCharsets.UTF_8);
FileUtils.writeStringToFile(new File(BASE, "sub/deep/b.log"), "b\n", StandardCharsets.UTF_8);
FileUtils.writeStringToFile(new File(BASE, "sub/deep/c.txt"), "c\n", StandardCharsets.UTF_8);
Iterator<File> it = FileUtils.iterateFiles(new File(BASE, "sub"), new String[]{"log"}, true);
List<String> hit = new ArrayList<>();
while (it.hasNext()) {
hit.add(it.next().getAbsolutePath());
}
System.out.println("iterateFiles(递归, 后缀log) 命中 " + hit.size() + " 个:");
hit.forEach(p -> System.out.println(" " + p));
try (Stream<File> st = FileUtils.streamFiles(new File(BASE, "sub"), true, "log")) {
System.out.println("streamFiles(递归, 后缀log) 流计数=" + st.count());
}
File treeCopy = new File(BASE, "sub-copy");
FileUtils.copyDirectory(new File(BASE, "sub"), treeCopy);
System.out.println("copyDirectory 后 sub-copy 目录大小(字节)=" + FileUtils.sizeOfDirectory(treeCopy)
+ ", 递归文件数=" + FileUtils.listFiles(treeCopy, null, true).size());
FileUtils.cleanDirectory(treeCopy);
System.out.println("cleanDirectory 后 sub-copy 内部条目数=" + treeCopy.list().length
+ ", 目录本身仍存在=" + treeCopy.isDirectory());
}
static void demoTailer() throws Exception {
System.out.println("---- [5] Tailer 追尾日志 ----");
File log = new File(BASE, "app.log");
FileUtils.writeStringToFile(log, "2026-10-14 10:00:00 INFO 启动\n", StandardCharsets.UTF_8);
List<String> captured = new ArrayList<>();
TailerListener listener = new TailerListenerAdapter() {
@Override
public void handle(String line) {
captured.add(line);
System.out.println(" Tailer 捕获: " + line);
}
};
// 延时 200ms 轮询一次,从文件开头开始跟随;create() 内部已提交后台线程执行
Tailer tailer = Tailer.create(log, listener, 200L, false);
Thread.sleep(600L);
FileUtils.writeStringToFile(log, "2026-10-14 10:00:05 INFO 追加一行\n", StandardCharsets.UTF_8, true);
FileUtils.writeStringToFile(log, "2026-10-14 10:00:10 ERROR 追加错误行\n", StandardCharsets.UTF_8, true);
Thread.sleep(1200L);
System.out.println(" Tailer 累计捕获行数=" + captured.size());
tailer.stop();
}
static void demoMonitor() throws Exception {
System.out.println("---- [6] FileAlterationMonitor 目录变更监控 ----");
final CountDownLatch latch = new CountDownLatch(3);
FileAlterationObserver observer = new FileAlterationObserver(BASE);
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileCreate(File file) {
System.out.println(" [监控] 创建: " + file.getName());
latch.countDown();
}
@Override
public void onFileChange(File file) {
System.out.println(" [监控] 修改: " + file.getName());
latch.countDown();
}
@Override
public void onFileDelete(File file) {
System.out.println(" [监控] 删除: " + file.getName());
latch.countDown();
}
});
// 每隔 400ms 扫描一次
FileAlterationMonitor monitor = new FileAlterationMonitor(400L, observer);
monitor.start();
File watch = new File(BASE, "watch-target.txt");
FileUtils.writeStringToFile(watch, "v1\n", StandardCharsets.UTF_8);
Thread.sleep(900L);
FileUtils.writeStringToFile(watch, "v2\n", StandardCharsets.UTF_8, true);
Thread.sleep(900L);
FileUtils.deleteQuietly(watch);
boolean ok = latch.await(5, TimeUnit.SECONDS);
System.out.println(" 监控事件全部到达=" + ok);
monitor.stop();
}
}
💡 技术拓展:Java 生态里做「文件/流工具」的库不止 Commons IO 一家,常见方案对比如下——
方案 定位 特点 适用场景 java.nio(JDK 原生) 基础 API 无第三方依赖,Files/Paths 覆盖大部分场景 简单读写、追求零依赖 Commons IO 通用 IO 工具集 IOUtils/FileUtils/FilenameUtils/Tailer/Monitor 齐全 日志追尾、目录监控、批量文件操作 Guava IO Google 工具库子模块 与 Guava 全家桶耦合,API 偏轻 已用 Guava 的工程 Okio Kotlin/流式 IO 面向现代流与 Buffer,Square 系 网络/序列化底层流处理 本文采用 Commons IO,理由是它对「文件树遍历、目录变更监控、日志追尾」这类运维/工程常见需求提供了开箱即用的封装(
FileUtils、FileAlterationMonitor、Tailer),且与 JDK 8+ 完全兼容、无额外交付负担。
4.7 编译与运行验证
在已导出 JAVA_HOME 的会话中执行 Maven 打包:
cd /root/commons-io-demo && mvn -B package
[INFO] Replacing original artifact with shaded artifact.
[INFO] Replacing /root/commons-io-demo/target/commons-io-demo.jar with /root/commons-io-demo/target/commons-io-demo-1.0.0-shaded.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.779 s
[INFO] Finished at: 2026-10-13T18:09:48Z
[INFO] ------------------------------------------------------------------------
ls -l target/*.jar
-rw-r--r-- 1 root root 622884 Oct 13 18:09 target/commons-io-demo.jar
-rw-r--r-- 1 root root 14865 Oct 13 18:09 target/original-commons-io-demo.jar
commons-io-demo.jar(约 623 KB)是内嵌 commons-io 的胖 jar,original-commons-io-demo.jar 是不含依赖的原始 jar。
运行并观察完整回显:
java -Dstdout.encoding=UTF-8 -Dfile.encoding=UTF-8 -jar target/commons-io-demo.jar
工作目录: /root/io-demo-work 存在=true
---- [1] FileUtils.writeStringToFile / readFileToString ----
Hello Commons IO
第二行 中文
第三行
文件字节数=44, 文本行数=3
---- [2] IOUtils.copy / copyLarge / toString ----
IOUtils.copy 传输字节数=44
复制后内容一致=true
IOUtils.copyLarge 缓冲区=4096 传输字节数=44
---- [3] FilenameUtils ----
getExtension(/opt/app/logs/app-2026.log) = log
getBaseName(/opt/app/logs/app-2026.log) = app-2026
normalize(/opt/app/../var//log/./x.txt) = /opt/var/log/x.txt
getFullPath(/opt/data/quzuo/docs/a.md) = /opt/data/quzuo/docs/
isExtension(archive.tar.gz, gz) = true
---- [4] FileUtils 目录遍历 / 复制 / 清理 ----
iterateFiles(递归, 后缀log) 命中 2 个:
/root/io-demo-work/sub/a.log
/root/io-demo-work/sub/deep/b.log
streamFiles(递归, 后缀log) 流计数=2
copyDirectory 后 sub-copy 目录大小(字节)=6, 递归文件数=3
cleanDirectory 后 sub-copy 内部条目数=0, 目录本身仍存在=true
---- [5] Tailer 追尾日志 ----
Tailer 捕获: 2026-10-14 10:00:00 INFO 启动
Tailer 捕获: 2026-10-14 10:00:05 INFO 追加一行
Tailer 捕获: 2026-10-14 10:00:10 ERROR 追加错误行
Tailer 累计捕获行数=3
---- [6] FileAlterationMonitor 目录变更监控 ----
[监控] 创建: watch-target.txt
[监控] 修改: watch-target.txt
[监控] 删除: watch-target.txt
监控事件全部到达=true
结果解读:六组演示全部按预期输出——文件读写与复制字节数一致(44 字节)、文件名规范化把 /opt/app/../var//log/./x.txt 还原为 /opt/var/log/x.txt、Tailer 捕获到追加的日志行、FileAlterationMonitor 捕获到创建/修改/删除三类事件。这表示 commons-io 2.22.0 已在 CentOS 8 的 JDK 17 上完整可用。
💡 说明:
-Dstdout.encoding=UTF-8 -Dfile.encoding=UTF-8用于固定中文输出的字符集。CentOS 8 默认platform encoding: ANSI_X3.4-1968(即 POSIX),省略该参数时中文输出会乱码。
五、方案 B:官方 bin 包 + jar 直跑(离线友好)
本节面向没有 Maven/构建体系的场景,直接使用官方发布的 jar 包,用 javac + java 完成编译运行,不依赖任何构建工具。该方案不受 4.4 节 Maven 绑定 JDK 问题影响,因为全程使用 javac/java 直跑。
5.1 方案说明
链路是:从 downloads.apache.org 下载官方 bin 包 → 校验 sha512 → 解压得到 commons-io-2.22.0.jar → 用 javac -cp 编译源码 → 用 java -cp 运行。适用于离线环境、脚本化部署或纯粹想「拿到 jar 就能用」的读者。
5.2 下载与完整性校验
cd /root && curl -sO --max-time 120 https://downloads.apache.org/commons/io/binaries/commons-io-2.22.0-bin.tar.gz
curl -sO --max-time 60 https://downloads.apache.org/commons/io/binaries/commons-io-2.22.0-bin.tar.gz.sha512
ls -l commons-io-2.22.0-bin.tar.gz
-rw-r--r-- 1 root root 9027936 Oct 13 18:13 commons-io-2.22.0-bin.tar.gz
校验 sha512(期望值与实测值必须逐字一致):
echo "期望: $(cat commons-io-2.22.0-bin.tar.gz.sha512)"
echo "实测: $(sha512sum commons-io-2.22.0-bin.tar.gz | cut -d' ' -f1)"
期望: aa8184f097ee9d43b2fbe56a82ca1ed3f0f4c153687cc18c4d218601948270a92a69c36224317db236715064946a3799727de5c3e57c536ddbaf61a98d84c737
实测: aa8184f097ee9d43b2fbe56a82ca1ed3f0f4c153687cc18c4d218601948270a92a69c36224317db236715064946a3799727de5c3e57c536ddbaf61a98d84c737
⚠️ 注意:若 sha512 不一致,说明下载包损坏或被篡改,须删除后重新下载;切勿跳过校验直接使用。
5.3 解压
tar xzf commons-io-2.22.0-bin.tar.gz && ls commons-io-2.22.0/
LICENSE.txt
NOTICE.txt
RELEASE-NOTES.txt
commons-io-2.22.0-javadoc.jar
commons-io-2.22.0-sources.jar
commons-io-2.22.0-test-sources.jar
commons-io-2.22.0-tests.jar
commons-io-2.22.0.jar
docs
主 jar 为 commons-io-2.22.0.jar(609182 字节,实测):
ls -l commons-io-2.22.0/commons-io-2.22.0.jar
-rw-r--r-- 1 root root 609182 Apr 19 11:39 commons-io-2.22.0/commons-io-2.22.0.jar
5.4 javac 编译与 java 运行
用官方 jar 作为编译与运行期 classpath:
mkdir -p /root/jar-run && cd /root/jar-run
cp /root/commons-io-2.22.0/commons-io-2.22.0.jar .
cp /root/commons-io-demo/src/main/java/Demo.java .
javac -encoding UTF-8 -cp commons-io-2.22.0.jar -d out Demo.java
Note: Demo.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
java -Dstdout.encoding=UTF-8 -Dfile.encoding=UTF-8 -cp out:commons-io-2.22.0.jar Demo | head -12
工作目录: /root/io-demo-work 存在=true
---- [1] FileUtils.writeStringToFile / readFileToString ----
Hello Commons IO
第二行 中文
第三行
文件字节数=44, 文本行数=3
---- [2] IOUtils.copy / copyLarge / toString ----
IOUtils.copy 传输字节数=44
复制后内容一致=true
IOUtils.copyLarge 缓冲区=4096 传输字节数=44
---- [3] FilenameUtils ----
getExtension(/opt/app/logs/app-2026.log) = log
验证点:java -cp 一行成功打印六组结果(此处节选前三组,后三组与 4.7 节完全一致),且无 NoClassDefFoundError,说明 jar 直跑可用;出现 deprecated API 提示属正常(Tailer.create 已在 2.22.0 标记为过时,推荐 Tailer.builder(),但功能完全可用)。
5.5 Maven 中央仓库 jar 的独立获取(可选)
若只想从 Maven 中央仓库获取单个 jar 而不使用构建工具,可直接下载并校验 sha1:
cd /root/jar-run && curl -sO --max-time 60 https://repo1.maven.org/maven2/commons-io/commons-io/2.22.0/commons-io-2.22.0.jar
curl -sO --max-time 30 https://repo1.maven.org/maven2/commons-io/commons-io/2.22.0/commons-io-2.22.0.jar.sha1
echo "期望: $(cat commons-io-2.22.0.jar.sha1)"
echo "实测: $(sha1sum commons-io-2.22.0.jar | cut -d' ' -f1)"
期望: 5b1e18bc0ad651ed878029d83f88d9c8189fd51e
实测: 5b1e18bc0ad651ed878029d83f88d9c8189fd51e
六、方案对比与选择建议
本节汇总两种方案的差异,给出选型建议。两种方案产出的是同一份 commons-io 2.22.0 代码,差别仅在获取与装配方式;方案 B 因全程用 javac/java 直跑,天然绕开了方案 A 在 CentOS 8 上必须处理的 JAVA_HOME 绑定问题。
| 维度 | 方案 A:Maven 依赖 | 方案 B:官方 bin 包 + jar 直跑 |
|---|---|---|
| 运行期依赖 | Maven 自动解析 | 手工把 jar 放入 classpath |
| 是否联网 | 首次构建需联网 | 仅下载时需要联网 |
| 依赖版本管控 | pom 统一声明,可锁版本 | 无,靠文件命名区分 |
| 多依赖协同(传递依赖) | 自动处理 | 需手工收集 |
| 可复现产物 | mvn package 产出胖 jar | javac 产出 class + 引用 jar |
| CentOS 8 特殊处理 | 需先设置 JAVA_HOME 指向 JDK 17 | 无需额外处理 |
| 适用场景 | 常规工程开发 | 离线脚本、临时验证、无构建体系 |
选型建议:若项目有构建体系(Maven/Gradle),一律选方案 A,但 CentOS 8 上务必先按 4.4 节设置 JAVA_HOME;若在无构建工具的老旧服务器上临时验证,或需要把 jar 手工嵌入既有脚本,选方案 B。两者也可共存——用方案 A 开发、用方案 B 分发 jar。
七、配置说明
本节说明 Commons IO 使用中真正需要设置的「配置」。它是库而非服务,没有独立配置文件,全部行为通过代码参数或 JDK 环境变量控制。
环境变量 JAVA_HOME(CentOS 8 上 Maven 依赖它定位 JDK 17,这是与 Ubuntu 最大的配置差异):
echo "JAVA_HOME=$JAVA_HOME"
JAVA_HOME=
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64 && echo "JAVA_HOME=$JAVA_HOME"
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64
逐项说明:
JAVA_HOME:Maven 定位 JDK 的优先来源。CentOS 8 的 Maven 3.5.4 启动脚本在JAVA_HOME为空时会通过set_jvm逻辑取到 Java 8 的 JRE(见 4.4 节),导致编译source/target = 17的工程报invalid target release: 17;显式导出该变量指向 JDK 17 后问题消失。建议写入~/.bashrc持久化。- 代码级「参数」:文件读写字符集(如
StandardCharsets.UTF_8)、缓冲区大小(IOUtils.DEFAULT_BUFFER_SIZE实测为 8192 字节)、Tailer轮询延时(默认 1000 ms)、FileAlterationMonitor扫描间隔(默认 10000 ms)等,均通过 API 入参控制,无外部配置文件。各参数的完整默认值见《Apache Commons IO 配置参数详解》。
八、多版本管理
多版本共存靠「隔离依赖坐标 + 隔离 jar 文件名」实现,无需切换全局软链。
Maven 本地仓库中不同版本的 commons-io 并列存放,互不覆盖(实时回显):
ls -1 $HOME/.m2/repository/commons-io/commons-io/
1.4
2.13.0
2.22.0
2.6
要点:工程要用哪个版本,只在 pom.xml 的 <dependency><version> 中改数字即可,其余版本仍留在本地仓库供其他工程复用。jar 直跑场景,把不同版本的 jar(如 commons-io-2.13.0.jar)分目录存放,运行时按需拼 classpath。
切换工程依赖版本并验证生效:
cd /root/commons-io-demo && mvn -q -B dependency:tree | grep commons-io
[INFO] \- commons-io:commons-io:jar:2.22.0:compile
九、常见问题(FAQ)
Q1:mvn -B package 报 invalid target release: 17 怎么办?
这是 Maven 正运行在 Java 8 上导致的(mvn -version 的 Java version 一行为 1.8.0_312)。解决:export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-17.0.1.0.12-2.el8_5.x86_64 后重新执行;需要持久化则写入 ~/.bashrc。
Q2:mvn -version 显示的 Java version 是 1.8,但 java -version 是 17?
正常现象。CentOS 8 的 Maven 3.5.4 通过 /usr/share/java-utils/java-functions 的 set_jvm 逻辑选 JVM,不读 PATH 中的 java。按 Q1 的方式显式设置 JAVA_HOME 即可切换。
Q3:mvn package 报 maven-jar-plugin:3.4.x requires Maven version 3.6.3 怎么办?
插件版本高于本机 Maven 版本导致。CentOS 8 自带 Maven 3.5.4,无法满足 3.6.3+ 要求。两种解法:一是 dnf module enable maven:3.6 后升级 Maven;二是改用兼容低版本 Maven 的插件。本文 pom.xml 已统一使用 maven-shade-plugin 3.5.1(要求 Maven 3.2.5+),实测可避免该问题。
Q4:运行时报 Could not find or load main class Demo?
说明运行期 classpath 未包含编译产物或 commons-io jar。jar 直跑时确认命令为 java -cp out:commons-io-2.22.0.jar Demo(out 为 javac -d 指定的输出目录);java -jar 方式则要确保已用 shade 打包出含 Demo.class 的胖 jar。
Q5:中文输出显示为乱码(问号)?
CentOS 8 默认 platform encoding: ANSI_X3.4-1968(POSIX),JVM 会用 ASCII 输出中文。解决:运行时加 -Dstdout.encoding=UTF-8 -Dfile.encoding=UTF-8,或把系统 locale 调成 UTF-8。
Q6:commons-io 2.22.0 能不能跑在 JDK 8 上? 可以。实测其字节码主版本号为 52(Java 8),JDK 8 及以上均可运行;本文用 JDK 17 属于更高基线,兼容性不受影响。
十、卸载与清理
Commons IO 本身不产生系统级安装,卸载即移除依赖与本地文件。
移除 Maven 工程、jar 直跑目录与下载包(清理某工程独有依赖):
rm -rf /root/commons-io-demo /root/jar-run /root/io-demo-work /root/commons-io-2.22.0 /root/commons-io-2.22.0-bin.tar.gz
ls /root/commons-io-demo 2>&1
ls: cannot access '/root/commons-io-demo': No such file or directory
移除 Maven 本地仓库中的 commons-io(影响所有工程,慎用):
rm -rf $HOME/.m2/repository/commons-io/commons-io/2.22.0
卸载 JDK 与 Maven(仅当不再需要)。先以 --assumeno 预演确认将删除的包(Maven 会连带移除大量依赖,本文实测共 135 个包、释放约 464 MB),无误后去掉该选项真正执行:
dnf remove --assumeno java-17-openjdk-devel maven
Transaction Summary
==================================================================================================================
Remove 135 Packages
Freed space: 464 M
Operation aborted.
确认无误后真正卸载(实际生产环境执行):
dnf remove -y java-17-openjdk-devel maven
十一、参考链接
- Apache Commons IO 官方主页:https://commons.apache.org/proper/commons-io/
- Apache Commons IO 官方下载目录:https://downloads.apache.org/commons/io/
- Commons IO 2.22.0 发布说明:https://downloads.apache.org/commons/io/RELEASE-NOTES.txt
- Commons IO Javadoc(IOUtils/FileUtils/FilenameUtils/Tailer):https://commons.apache.org/proper/commons-io/apidocs/
- Maven 中央仓库 commons-io 坐标:https://repo1.maven.org/maven2/commons-io/commons-io/
- OpenJDK 17 安装包(CentOS 8 AppStream 源):
dnf info java-17-openjdk-devel