1、概述
commons-io(Apache Commons IO)是 Java 处理 IO 操作最强力的工具库之一,全面增强了原生的 java.io 与 java.nio 包。它消除了繁重冗长的流读写、字节缓冲区操作以及手动关闭流的代码,彻底规避了内存溢出和资源泄漏问题。
核心 API 分类列表全景图
| 分类模块 | 常用类 (Class) | 核心功能与解决问题 |
|---|---|---|
| 流操作 (Stream) | IOUtils | 输入/输出流的读写、复制、转换与安全关闭(最常用) |
| 文件与目录 (File) | FileUtils | 文件/目录的创建、删除、复制、移动、读取与写入 |
| 文件名与路径 (Path) | FilenameUtils | 跨平台的路径提取、扩展名获取、路径规范化 |
| 文件监控 (Monitor) | FileAlterationObserver, FileAlterationListenerAdaptor | 监控本地文件或目录的创建、修改、删除等动态 |
| 文件过滤 (Filter) | IOFileFilter, FileFilterUtils, NameFileFilter | 组合式文件过滤器,配合目录遍历或搜寻 |
| 比较与校验 (Compare) | ContentEqualsFactory, CRC64 | 比较文件内容是否一致,计算文件 checksum 等 |
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.16.1</version>
</dependency>
2、流操作
2.1、IOUtils(流操作与安全转换)
commons-io 中的 org.apache.commons.io.IOUtils 是 Java 开发中最强大的 I/O 流处理工具类之一。它针对输入/输出流(InputStream、OutputStream、Reader、Writer)提供了大量静态方法,特点在于高性能、代码简洁、天然防范 NPE,且底层自动处理了缓冲(Buffer)逻辑。
2.1.1、核心API
- 流转数据(Read)
toString(InputStream input, Charset encoding) / toString(URI uri, Charset encoding):将流或资源一次性转为字符串。toByteArray(InputStream input):将输入流转换为 byte[]。readLines(InputStream input, Charset encoding):按行读取流内容,返回 List<String>。
- 数据写入流(Write)
write(String data, OutputStream output, Charset encoding):将字符串写入输出流。writeLines(Collection<?> lines, String lineEnding, OutputStream output, Charset encoding):将集合逐行写入输出流。
- 流数据对复制(Copy - 核心功能)
copy(InputStream input, OutputStream output):高效将输入流复制到输出流(内部使用默认 4KB 缓冲区,支持超过 2GB 的数据,返回 int,超大流会自动转为使用 copyLarge)。copyLarge(InputStream input, OutputStream output):专门用于处理超大流复制,返回类型为 long。
- 流比较与控制
contentEquals(InputStream input1, InputStream input2):比对两个流的内容是否完全一致。consume(InputStream input):读取并丢弃流中的所有剩余数据。skip(InputStream input, long toSkip):精确跳过指定字节数。
注意:较早版本中的 IOUtils.closeQuietly() 已在现代版本中被标注为废弃,推荐优先使用 Java 7+ 的 try-with-resources 语法进行资源的自动关闭。
2.1.2、使用示例
示例 1:流转字符串与行读取(读取配置文件/响应流)
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class IOUtilsReadDemo {
public static void main(String[] args) {
// 从 Classpath 读取文件流
try (InputStream input = IOUtilsReadDemo.class.getResourceAsStream("/config.json")) {
if (input != null) {
// 1. 将 InputStream 直接转换为 UTF-8 字符串
String jsonStr = IOUtils.toString(input, StandardCharsets.UTF_8);
System.out.println("文本内容:\n" + jsonStr);
}
} catch (Exception e) {
e.printStackTrace();
}
// 按行读取
try (InputStream logStream = IOUtilsReadDemo.class.getResourceAsStream("/app.log")) {
if (logStream != null) {
// 2. 按行读取返回 List<String>
List<String> lines = IOUtils.readLines(logStream, StandardCharsets.UTF_8);
System.out.println("日志总行数: " + lines.size());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
示例 2:高效流复制(网络文件下载 / 响应导出)
IOUtils.copy 内部使用了高性能的缓冲池,无需手动循环写 byte[4096]。
import org.apache.commons.io.IOUtils;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
public class IOUtilsCopyDemo {
public static void main(String[] args) {
String fileUrl = "https://example.com/files/sample.pdf";
// 从网络 URL 打开输入流,并复制保存到本地文件
try (InputStream in = new URL(fileUrl).openStream();
OutputStream out = new FileOutputStream("downloaded_sample.pdf")) {
// 高效复制流(内部自动处理 Buffer)
long bytesCopied = IOUtils.copyLarge(in, out);
System.out.println("文件下载成功,传输字节数: " + bytesCopied);
} catch (Exception e) {
e.printStackTrace();
}
}
}
示例 3:集合数据逐行写入流
适合用于导出 CSV、日志文件或构造 HTTP 输出流。
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
public class IOUtilsWriteDemo {
public static void main(String[] args) throws Exception {
List<String> records = Arrays.asList(
"ID,Name,Role",
"101,Alice,Admin",
"102,Bob,User"
);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// 1. 将集合按行写入输出流(自动处理换行符)
IOUtils.writeLines(records, System.lineSeparator(), out, StandardCharsets.UTF_8);
// 2. 验证写入的内容
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
String resultStr = IOUtils.toString(in, StandardCharsets.UTF_8);
System.out.println("生成的内容:\n" + resultStr);
}
}
生成的内容:
ID,Name,Role
101,Alice,Admin
102,Bob,User
示例 4:流内容相同性校验
比较两个流的字节内容是否完全等价(常用于校验文件内容或数据传输完整性)。
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class IOUtilsCompareDemo {
public static void main(String[] args) throws Exception {
InputStream stream1 = new ByteArrayInputStream("Hello World".getBytes(StandardCharsets.UTF_8));
InputStream stream2 = new ByteArrayInputStream("Hello World".getBytes(StandardCharsets.UTF_8));
InputStream stream3 = new ByteArrayInputStream("Hello Java".getBytes(StandardCharsets.UTF_8));
// 比对内容是否一致
boolean isEqual12 = IOUtils.contentEquals(stream1, stream2); // true
boolean isEqual13 = IOUtils.contentEquals(stream1, stream3); // false
System.out.println("Stream 1 & 2 是否相同: " + isEqual12);
System.out.println("Stream 1 & 3 是否相同: " + isEqual13);
}
}
Stream 1 & 2 是否相同: true
Stream 1 & 3 是否相同: false
3、文件与目录
apache commons-io 在处理本地文件与目录时,主要通过 FileUtils(文件与目录高级工具)和 FilenameUtils(文件名与路径解析工具)这两个核心类来实现。它们极大地补充了原生 java.io.File 功能的不足,简化了读写、复制、移动、递归删除以及路径规范化等操作。
3.1、FileUtils(文件与目录高级操作)
FileUtils 提供了针对 File 对象的全套读写、复制、删除与遍历 API。其最大特点是自动管理底层文件流的开启与关闭,无需编写繁琐的 FileInputStream 或 FileOutputStream 代码。
3.1.1、核心API
- 文件读写操作
readFileToString(File file, Charset encoding) / readFileToByteArray(File file):将文件内容一次性读取为字符串或字节数组。readLines(File file, Charset encoding):按行读取文件,返回 List<String>。writeStringToFile(File file, String data, Charset encoding, boolean append):将字符串写入文件(append 为 true 表示追加模式)。writeLines(File file, Collection<?> lines, boolean append):将集合逐行写入文件。
- 文件与目录的复制 / 移动
copyFile(File srcFile, File destFile):复制文件,可保留源文件修改时间。copyDirectory(File srcDir, File destDir):递归复制整个目录及其所有子内容。copyFileToDirectory(File srcFile, File destDir):将文件复制到指定目录下。moveFile(File srcFile, File destFile) / moveDirectory(File srcDir, File destDir):剪切/移动文件或目录。
- 删除与清理
deleteDirectory(File directory):强力递归删除非空目录(原生 File.delete() 无法直接删除带内容的目录)。cleanDirectory(File directory):清空目录下的所有内容,但保留目录本身。deleteQuietly(File file):安全删除文件或目录,不抛出任何 Exception(若删除失败仅返回 false)。
- 目录查找与计算
sizeOf(File fileOrDir) / sizeOfDirectory(File directory):精确计算文件或整个目录的占用字节大小。listFiles(File directory, IOFileFilter fileFilter, IOFileFilter dirFilter):结合文件过滤器高阶搜索子文件。
3.1.2、使用示例
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.SuffixFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
public class FileUtilsDemo {
public static void main(String[] args) {
try {
File srcFile = new File("app.log");
File backupDir = new File("backup");
// 1. 快速写入与追加文本
FileUtils.writeStringToFile(srcFile, "2026-09-04 [INFO] 系统启动完成\n", StandardCharsets.UTF_8, true);
// 2. 读取文本与按行读取
String content = FileUtils.readFileToString(srcFile, StandardCharsets.UTF_8);
List<String> lines = FileUtils.readLines(srcFile, StandardCharsets.UTF_8);
System.out.println("日志总行数: " + lines.size());
// 3. 文件与目录复制/移动
FileUtils.copyFileToDirectory(srcFile, backupDir); // 复制 app.log 到 backup/ 目录下
// 4. 计算目录大小(转换为 MB)
long dirSize = FileUtils.sizeOfDirectory(backupDir);
System.out.println("备份目录总大小: " + (dirSize / 1024) + " KB");
// 5. 条件递归搜索:查找 backup 目录下所有 .log 后缀的文件
Collection<File> logFiles = FileUtils.listFiles(
backupDir,
new SuffixFileFilter(".log"), // 文件过滤器
TrueFileFilter.INSTANCE // 目录过滤器(允许进入所有子目录搜索)
);
System.out.println("找到的 log 文件数量: " + logFiles.size());
// 6. 安全清理与删除目录
// FileUtils.deleteDirectory(backupDir); // 递归删除整个目录
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.2、FilenameUtils(文件名与路径解析)
跨平台(Windows / Linux)处理文件名与路径时,直接使用 String.substring 或 split 极易出现分隔符不一致的问题。FilenameUtils 提供了与平台解耦的路径与文件名提取工具。
3.2.1、核心API
getExtension(String filename):快速提取扩展名(如 txt、png),无扩展名则返回空字符串。getBaseName(String filename):获取无扩展名的主文件名。getName(String filename):提取带扩展名的完整文件名。getFullPath(String filename):获取包含盘符和层级目录的路径。normalize(String filename):规范化路径(自动清理 ., … 以及纠正系统斜杠类型)。isExtension(String filename, String extension):检查文件扩展名是否符合预期。
3.2.2、使用示例
import org.apache.commons.io.FilenameUtils;
public class FilenameUtilsDemo {
public static void main(String[] args) {
String rawPath = "D:/data/project/../logs/sys_2026.log";
// 1. 路径规范化(自动处理 relative 符号 .. 并纠正正反斜杠)
String normalizedPath = FilenameUtils.normalize(rawPath);
System.out.println("规范化路径: " + normalizedPath); // D:\data\logs\sys_2026.log
// 2. 文件名要素提取
System.out.println("文件扩展名: " + FilenameUtils.getExtension(rawPath)); // log
System.out.println("主文件名: " + FilenameUtils.getBaseName(rawPath)); // sys_2026
System.out.println("完整文件名: " + FilenameUtils.getName(rawPath)); // sys_2026.log
System.out.println("父级路径: " + FilenameUtils.getFullPath(rawPath)); // D:/data/project/../logs/
// 3. 校验扩展名
boolean isImage = FilenameUtils.isExtension(rawPath, new String[]{"png", "jpg", "jpeg"});
System.out.println("是否为图片文件: " + isImage); // false
}
}
4、文件监控
commons-io 提供了专门用于实时监控文件与目录变更(新建、修改、删除)的事件驱动组件包 org.apache.commons.io.monitor。相比传统的“轮询遍历文件列表”或原生 Java NIO 的 WatchService,commons-io 的文件监控框架 API 设计更加高级且易用,支持针对指定目录的递归监听、防刷抖动处理以及开箱即用的事件回调。
4.1、核心设计架构与四大组件
文件监控机制由以下四个关键类协同工作:
- FileAlterationObserver(观察者)
- 职责:绑定一个监控根目录。负责扫描该目录树,并在内部保存文件系统的状态快照。
- 工作逻辑:每次触发检查时,对当前目录状态与上一次快照进行比对,若发现差异则触发对应的监听事件。支持传入 IOFileFilter 进行文件过滤。
- FileAlterationListener / FileAlterationListenerAdaptor(事件监听器)
- 职责:定义具体的事件回调逻辑。
- 常见方法:onFileCreate(文件创建)、onFileChange(文件修改)、onFileDelete(文件删除)、onDirectoryCreate(目录创建)等。
- 最佳实践:推荐继承抽象类 FileAlterationListenerAdaptor,只需重写业务感兴趣的方法,无需实现全量接口。
- FileAlterationMonitor(监控定时器)
- 职责:后台异步线程管理器。按照设定的时间间隔(如 1 秒/5 秒)定时调用关联的 FileAlterationObserver.checkAndNotify() 方法。
- IOFileFilter(文件过滤器 - 可选)
- 职责:限定观察者只关注特定类型或后缀的文件(如只监听 .json 或 .conf 文件的变动)。
4.2、核心API
- FileAlterationObserver 构建
FileAlterationObserver.builder().setFile(File directory).setFileFilter(IOFileFilter filter).get():推荐使用 Builder 模式创建观察者。addListener(FileAlterationListener listener):为观察者注册事件监听器。
- FileAlterationMonitor 控制
new FileAlterationMonitor(long intervalMillis, FileAlterationObserver... observers):创建监控线程,指定轮询间隔(毫秒)。start():启动后台监控线程。stop() / stop(long stopInterval):停止监控线程并释放资源。
4.3、使用示例
以下示例展示了如何实现对指定配置目录(./config)下 .properties 文件变化的实时监听,常用于本地配置热加载场景。
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
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.util.concurrent.TimeUnit;
public class FileMonitorDemo {
public static void main(String[] args) throws Exception {
// 监控的目标目录
File monitorDir = new File("./config");
if (!monitorDir.exists()) {
monitorDir.mkdirs();
}
// 1. 定义文件过滤器:过滤掉隐藏文件,且只监听以 .properties 结尾的文件
IOFileFilter visibleFiles = HiddenFileFilter.VISIBLE;
IOFileFilter propertiesFiles = FileFilterUtils.suffixFileFilter(".properties");
IOFileFilter combinedFilter = FileFilterUtils.and(visibleFiles, propertiesFiles);
// 2. 创建观察者(Observer),绑定目录与过滤器
FileAlterationObserver observer = FileAlterationObserver.builder()
.setFile(monitorDir)
.setFileFilter(combinedFilter)
.get();
// 3. 注册事件监听器(Listener),重写对应的回调逻辑
observer.addListener(new FileAlterationListenerAdaptor() {
@Override
public void onFileCreate(File file) {
System.out.println("【文件新建】: " + file.getAbsolutePath());
// TODO: 触发配置初始化加载
}
@Override
public void onFileChange(File file) {
System.out.println("【文件修改】: " + file.getAbsolutePath());
// TODO: 触发配置热刷新逻辑
}
@Override
public void onFileDelete(File file) {
System.out.println("【文件删除】: " + file.getAbsolutePath());
// TODO: 触发清理缓存逻辑
}
@Override
public void onDirectoryCreate(File directory) {
System.out.println("【目录新建】: " + directory.getAbsolutePath());
}
@Override
public void onDirectoryDelete(File directory) {
System.out.println("【目录删除】: " + directory.getAbsolutePath());
}
});
// 4. 创建监控后台线程(Monitor),设置轮询时间间隔为 3 秒
long interval = TimeUnit.SECONDS.toMillis(3);
FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
// 5. 启动监控线程
System.out.println(">>> 开始监听目录: " + monitorDir.getAbsolutePath() + " (轮询间隔 3 秒)...");
monitor.start();
// 模拟主线程持续运行一段时间以展示效果
Thread.sleep(30000);
// 6. 优雅关闭监控线程
System.out.println(">>> 停止文件监控器...");
monitor.stop();
}
}
5、文件过滤
apache commons-io 在 org.apache.commons.io.filefilter 包下提供了一套强大且易用的文件过滤器(File Filter)体系。
相比原生 java.io.FileFilter,commons-io 的过滤器不仅内置了按后缀、文件名模式、文件大小、修改时间、隐蔽性等高频筛选逻辑,还支持布尔逻辑组合(与、或、非),并能够同时作用于文件与目录的筛选。
5.1、核心接口与常用过滤器分类
所有过滤器均实现了 IOFileFilter 接口(继承自原生的 FileFilter 和 FilenameFilter)。
常用基础过滤器
-
SuffixFileFilter / PrefixFileFilter:基于文件后缀(如 .log)或前缀(如 sys_)进行匹配。 -
WildcardFileFilter:支持通配符(如 “*.txt”、“data_2026_??.csv”)。 -
RegexFileFilter:使用正则表达式匹配文件名。 -
SizeFileFilter:基于文件大小筛选(例如大于 10MB 或小于 1KB)。 -
AgeFileFilter:基于修改时间筛选(例如 7 天前修改的文件,可搭配 CutoffFilter)。 -
HiddenFileFilter:过滤隐藏文件或非隐藏文件(HiddenFileFilter.HIDDEN / HIDDEN.VISIBLE)。 -
DirectoryFileFilter / FileFileFilter:专门匹配“仅目录”或“仅文件”(DirectoryFileFilter.DIRECTORY / FileFileFilter.FILE)。 -
TrueFileFilter / FalseFileFilter:恒真/恒假匹配(用于递归搜索时放行所有子目录)。 -
逻辑组合过滤器(组合拳)
-
AndFileFilter或FileFilterUtils.and(...):逻辑与(同时满足多个条件)。 -
OrFileFilter或FileFilterUtils.or(...):逻辑或(满足任意条件即可)。 -
NotFileFilter或FileFilterUtils.not(...):逻辑非(取反操作)。
5.2、使用示例
示例 1:基础过滤器与逻辑组合
通过 FileFilterUtils 工厂类,可以极其优雅地以链式或静态方法构建复杂组合过滤器。
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import java.io.File;
public class BasicFilterDemo {
public static void main(String[] args) {
File targetDir = new File("./logs");
// 构建组合条件:
// 1. 必须是文件(排除目录)
// 2. 必须以 "app_" 开头 且 以 ".log" 结尾
// 3. 文件大小不能大于 5MB
IOFileFilter isFile = FileFilterUtils.fileFileFilter();
IOFileFilter namePattern = FileFilterUtils.and(
FileFilterUtils.prefixFileFilter("app_"),
FileFilterUtils.suffixFileFilter(".log")
);
IOFileFilter sizeFilter = FileFilterUtils.not(
FileFilterUtils.sizeFileFilter(5 * 1024 * 1024L, true) // acceptLarger = true
);
// 使用 and 链接所有条件
IOFileFilter combinedFilter = FileFilterUtils.and(isFile, namePattern, sizeFilter);
// 使用原生 File.listFiles 配合过滤器进行单层扫描
File[] matchedFiles = targetDir.listFiles(combinedFilter);
if (matchedFiles != null) {
for (File f : matchedFiles) {
System.out.println("符合条件的文件: " + f.getName());
}
}
}
}
示例 2:搭配 FileUtils.listFiles 递归检索与清理日志
FileUtils.listFiles 接收两个 IOFileFilter 参数:
- fileFilter:作用于文件粒度的筛选。
- dirFilter:决定是否递归进入某个子目录(传入 TrueFileFilter.INSTANCE 表示深入所有子目录)。
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import org.apache.commons.io.filefilter.WildcardFileFilter;
import java.io.File;
import java.util.Collection;
public class FileUtilsListDemo {
public static void main(String[] args) {
File rootDir = new File("./data");
// 查找规则:
// 1. 使用通配符匹配 '*.csv' 或 '*.xlsx'
// 2. 忽略隐藏目录/隐藏文件
IOFileFilter wildcardFilter = new WildcardFileFilter("*.csv", "*.xlsx");
IOFileFilter visibleFilter = FileFilterUtils.and(
wildcardFilter,
FileFilterUtils.not(FileFilterUtils.prefixFileFilter("."))
);
// 执行递归搜寻
Collection<File> files = FileUtils.listFiles(
rootDir,
visibleFilter, // 文件过滤条件
TrueFileFilter.INSTANCE // 目录过滤条件:递归搜寻所有子目录
);
System.out.println("成功找到相符文件数: " + files.size());
files.forEach(file -> System.out.println("Path: " + file.getAbsolutePath()));
}
}
示例 3:时间过滤器(按修改时间删除过期历史文件)
按修改时间筛选在清理历史日志、过期临时文件时属于刚需逻辑。
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.AgeFileFilter;
import org.apache.commons.io.filefilter.TrueFileFilter;
import java.io.File;
import java.util.Collection;
import java.util.Date;
public class AgeFilterDemo {
public static void main(String[] args) {
File logDir = new File("./logs");
// 计算 7 天前的毫秒时间戳
long sevenDaysAgoMillis = System.currentTimeMillis() - (7L * 24 * 60 * 60 * 1000);
Date cutoffDate = new Date(sevenDaysAgoMillis);
// 创建 AgeFileFilter:acceptOld = true 表示匹配修改时间【早于/老于】指定时间的文件
AgeFileFilter oldFileFilter = new AgeFileFilter(cutoffDate, true);
// 搜寻 logs 目录下所有 7 天前的历史文件
Collection<File> expiredLogs = FileUtils.listFiles(logDir, oldFileFilter, TrueFileFilter.INSTANCE);
for (File logFile : expiredLogs) {
System.out.println("清理过期日志: " + logFile.getName());
// FileUtils.deleteQuietly(logFile); // 安全删除过期文件
}
}
}
6、比较与校验
apache commons-io 在处理文件和流的内容比对、校验与比对测试时,提供了极具针对性的 API,主要分布在 IOUtils、FileUtils 以及专门用于校验计算的 org.apache.commons.io.comparator 和 org.apache.commons.io.input 包中。
6.1、内容流与文件的相等性比对(Content Comparison)
在判断两个文件或数据流内容是否相同时,直接对比文件名或文件大小并不准确。commons-io 提供了逐字节高效比对内容的方法,底层会自动使用缓冲处理,避免将大文件一次性加载到内存中。
6.1.1、核心 API
IOUtils.contentEquals(InputStream input1, InputStream input2):比对两个输入流中的字节内容是否完全一致。IOUtils.contentEqualsIgnoreEOL(Reader input1, Reader input2):按字符比对两个 Reader,忽略换行符(EOL, End-of-Line)的差异(例如 Windows 的 \r\n 与 Linux 的 \n 在内容一致时会被判定为相等)。FileUtils.contentEquals(File file1, File file2):比对两个文件的字节内容是否一致。FileUtils.contentEqualsIgnoreEOL(File file1, File file2, String encoding):忽略换行符差异比对两个文本文件。
6.1.2、使用示例
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class ContentCompareDemo {
public static void main(String[] args) throws Exception {
// 1. 比对字节流内容
InputStream stream1 = new ByteArrayInputStream("Data123".getBytes(StandardCharsets.UTF_8));
InputStream stream2 = new ByteArrayInputStream("Data123".getBytes(StandardCharsets.UTF_8));
boolean streamEqual = IOUtils.contentEquals(stream1, stream2);
System.out.println("流内容是否一致: " + streamEqual); // true
// 2. 文本文件跨平台比对(忽略 Windows \r\n 与 Linux \n 差异)
File fileWindows = new File("config_win.txt"); // 内容: line1\r\nline2
File fileLinux = new File("config_linux.txt"); // 内容: line1\nline2
// 普通字节比对(结果为 false)
boolean byteEqual = FileUtils.contentEquals(fileWindows, fileLinux);
// 忽略换行符比对(结果为 true)
boolean ignoreEolEqual = FileUtils.contentEqualsIgnoreEOL(fileWindows, fileLinux, "UTF-8");
System.out.println("严格字节比对: " + byteEqual);
System.out.println("忽略换行符比对: " + ignoreEolEqual);
}
}
6.2、流校验码计算(ChecksumInputStream & CRC32)
在网络传输或文件写盘时,需要计算数据的 CRC32 或其它校验码以确保数据的准确性(防止数据损坏)。commons-io 提供了 ChecksumInputStream,允许你在流读取数据的同时顺便计算出 CRC32 校验和,无需二次读取文件。
6.2.1、核心API
ChecksumInputStream:装饰器流,继承自 ProxyInputStream。ChecksumInputStream.builder():推荐使用链式 Builder 创建,传入 InputStream 以及 java.util.zip.Checksum(如 CRC32 或 Adler32)。getChecksum().getValue():获取实时计算出的校验码数值。
6.2.2、使用示例
import org.apache.commons.io.IOUtils;
import org.apache.commons.io.input.ChecksumInputStream;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.zip.CRC32;
public class ChecksumDemo {
public static void main(String[] args) throws Exception {
byte[] data = "Important Financial Record".getBytes(StandardCharsets.UTF_8);
InputStream sourceStream = new ByteArrayInputStream(data);
// 绑定 CRC32 校验算法至输入流
CRC32 crc32 = new CRC32();
try (ChecksumInputStream checksumIn = ChecksumInputStream.builder()
.setInputStream(sourceStream)
.setChecksum(crc32)
.get()) {
// 在读取/消费流的过程中,底层会自动累加计算 CRC32
String content = IOUtils.toString(checksumIn, StandardCharsets.UTF_8);
// 获取计算出的 CRC32 校验和
long calculatedChecksum = checksumIn.getChecksum().getValue();
System.out.println("读取到的内容: " + content);
System.out.println("实时计算出的 CRC32 校验和: " + calculatedChecksum);
}
}
}
6.3、文件列表排序与比较器(File Comparators)
在对目录中的文件进行排序时(例如:按修改时间清理旧日志、按文件大小排序、按文件名升序/降序),直接手写 Comparator 容易忽视 null 值或跨平台路径问题。commons-io 在 org.apache.commons.io.comparator 包中内置了丰富的预设比较器。
6.3.1、常用比较器类
NameFileComparator:按文件名排序(支持敏感/不敏感大小写 NAME_INSENSITIVE_COMPARATOR)。SizeFileComparator:按文件/目录占用空间大小排序(SIZE_COMPARATOR / SIZE_REVERSE)。LastModifiedFileComparator:按最后修改时间排序(LASTMODIFIED_COMPARATOR / LASTMODIFIED_REVERSE)。ExtensionFileComparator:按文件扩展名排序。CompositeFileComparator:组合多个比较器(例如:先按修改时间排序,若相同再按文件名排序)。
6.3.2、使用示例
import org.apache.commons.io.comparator.CompositeFileComparator;
import org.apache.commons.io.comparator.LastModifiedFileComparator;
import org.apache.commons.io.comparator.NameFileComparator;
import org.apache.commons.io.comparator.SizeFileComparator;
import java.io.File;
import java.util.Arrays;
import java.util.List;
public class FileComparatorDemo {
public static void main(String[] args) {
File logDir = new File("./logs");
File[] files = logDir.listFiles();
if (files != null) {
// 1. 按文件大小降序排序(大文件在前)
Arrays.sort(files, SizeFileComparator.SIZE_REVERSE);
System.out.println("--- 按文件大小降序排序 ---");
for (File f : files) {
System.out.println(f.getName() + " (" + f.length() + " bytes)");
}
// 2. 按修改时间升序排序(最旧的文件在前,适合日志清理)
Arrays.sort(files, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
// 3. 组合比较器:优先按修改时间排序,若修改时间相同则按文件名排序
CompositeFileComparator compositeComparator = new CompositeFileComparator(
LastModifiedFileComparator.LASTMODIFIED_COMPARATOR,
NameFileComparator.NAME_INSENSITIVE_COMPARATOR
);
List<File> fileList = Arrays.asList(files);
compositeComparator.sort(fileList);
}
}
}
&spm=1001.2101.3001.5002&articleId=164371733&d=1&t=3&u=92603925f0af4a95963135feb25b3256)
556

被折叠的 条评论
为什么被折叠?



