Apache Commons IO 是 Apache 软件基金会的一个开源项目,提供了一组用于处理 I/O 操作的实用工具类。它的目标是简化常见的文件和流操作,提供更方便、更高效的方式来处理输入和输出。
github 地址:https://github.com/apache/commons-io
官网地址:https://commons.apache.org/proper/commons-io/
pom依赖:
<dependency>
<groupId>commons-iogroupId>
<artifactId>commons-ioartifactId>
<version>2.13.0version>
dependency>
InputStream in = new URL( "https://commons.apache.org" ).openStream();
try {
// 将流内内容转换为字符串
System.out.println(IOUtils.toString(in, Charset.defaultCharset()));
} finally {
// 安全关闭流
IOUtils.closeQuietly(in);
}
inputStream = new FileInputStream("file.txt");
try {
// 消耗输入流内容,并忽视它
IOUtils.consume(inputStream);
} finally {
IOUtils.closeQuietly(inputStream);
}
try (InputStream inputStream1 = Files.newInputStream(file1.toPath());
InputStream inputStream2 = Files.newInputStream(file2.toPath())) {
// 比较流内容相等,还有个方法contentEqualsIgnoreEO,可以忽略行尾换行符,windows/linux/mac文件差异
boolean contentEquals = IOUtils.contentEquals(inputStream1, inputStream2);
if (contentEquals) {
System.out.println("文件内容相同");
} else {
System.out.println("文件内容不同");
}
} catch (IOException e) {
// 处理异常
}
File sourceFile = new File("source.txt");
File targetFile = new File("target.txt");
try (InputStream inputStream = Files.newInputStream(sourceFile.toPath());
OutputStream outputStream = Files.newOutputStream(targetFile.toPath())) {
// 从一个输入流复制数据到一个输出流,数据量大(超过2G)可以使用 copyLarge
long bytesCopied = IOUtils.copy(inputStream, outputStream);
System.out.println("已复制 " + bytesCopied + " 字节");
} catch (IOException e) {
// 处理异常
}
// 将输入流或输出流包装成带有缓冲功能的流
// 作用是为了优化数据的读取和写入性能。对于大量数据的读取和写入操作,使用缓冲流可以减少对底层资源(如磁盘)的频繁访问,从而提高整体的执行效率。
InputStream inputStream = new FileInputStream("input.txt");
BufferedInputStream bufferedInputStream = IOUtils.buffer(inputStream);
File file = new File("file.txt");
try (LineIterator iterator = IOUtils.lineIterator(new FileReader(file))) {
while (iterator.hasNext()) {
String line = iterator.next();
// 处理每行文本内容
System.out.println(line);
}
} catch (IOException e) {
// 处理异常
}
// 读取文件所有行
List<String> lines = IOUtils.readLines(new FileReader(file));
// 将每行内容写入文件
List<String> lines = new ArrayList<>();
lines.add("Line 1");
lines.add("Line 2");
lines.add("Line 3");
File outputFile = new File("output.txt");
try (OutputStream outputStream = Files.newOutputStream(outputFile.toPath());
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream, StandardCharsets.UTF_8))) {
IOUtils.writeLines(lines, System.lineSeparator(), writer);
} catch (IOException e) {
// 处理异常
}
// 读文件
FileUtils.readFileToString(file1, Charset.defaultCharset());
// 写文件
List<String> lines = new ArrayList<>();
lines.add("Line 1");
lines.add("Line 2");
lines.add("Line 3");
File outputFile = new File("output.txt");
FileUtils.writeLines(outputFile, StandardCharsets.UTF_8.name(), lines);
// 获取当前目录下,扩展名为xml和java的文件,true表示递归扩展子文件夹
Collection<File> files = FileUtils.listFiles(new File("."), new String[]{"xml", "java"}, true);
System.out.println(files);
// 复制文件
FileUtils.copyFile(new File("pom.xml"), new File("../pom.xml.bak"));
// 复制目录
FileUtils.copyDirectory(new File("."), new File("../test"));
// 移动文件
FileUtils.moveFile(new File("pom.xml"), new File("../pom.xml"));
// 移动文件到目录中,true为创建目录
FileUtils.moveFileToDirectory(new File("pom.xml"), new File("F://dic"), true);
// 移动到目录,可以是文件,也可以是目录
FileUtils.moveToDirectory(new File("D://test"), new File("F://test"), true);
// 移动目录
FileUtils.moveDirectory(new File("D://test"), new File("F://test"));
// 移动目录,true表示创建目的目录
FileUtils.moveDirectoryToDirectory(new File("D://test"), new File("F://test"), true);
// 删除文件,不能删除会抛异常
FileUtils.delete(new File("1.txt"));
// 删除目录
FileUtils.deleteDirectory(new File("../test"));
// 删除文件或目录,目录不能为空,不能删除时抛异常
FileUtils.forceDelete(new File("../test"));
boolean isFileOlder = FileUtils.isFileOlder(new File("pom.xml"), LocalDateTime.of(2023, 8, 29, 12, 12, 12));
System.out.println(isFileOlder);
boolean isFileNewer = FileUtils.isFileNewer(new File("pom.xml"), LocalDateTime.of(2023, 8, 29, 12, 12, 12));
System.out.println(isFileNewer);
long size = FileUtils.sizeOf(new File("pom.xml"));
System.out.println(size);
System.out.println(FileUtils.byteCountToDisplaySize(size));
long directorySize = FileUtils.sizeOfDirectory(new File("../"));
System.out.println(directorySize);
System.out.println(FileUtils.byteCountToDisplaySize(directorySize));
long checksumCRC32 = FileUtils.checksumCRC32(new File("pom.xml"));
System.out.println(checksumCRC32);
FileUtils.contentEquals(new File("1.txt"), new File("1.txt"));
FileUtils.contentEqualsIgnoreEOL(new File("1.txt"), new File("1.txt"), StandardCharsets.UTF_8.name());
// 定义一个监听类
class MyFileListener extends FileAlterationListenerAdaptor {
// 文件创建事件
@Override
public void onFileCreate(File file) {
System.out.println("文件创建:" + file.getAbsolutePath());
}
// 文件修改事件
@Override
public void onFileChange(File file) {
System.out.println("文件修改:" + file.getAbsolutePath());
}
// 文件删除事件
@Override
public void onFileDelete(File file) {
System.out.println("文件删除:" + file.getAbsolutePath());
}
}
final long interval = 100;
final String testDir = "D://test";
// 建立一个观察者
final FileAlterationObserver observer = new FileAlterationObserver(testDir);
observer.addListener(new MyFileListener());
// 建立一个监控者,监控间隔,单位毫秒
final FileAlterationMonitor monitor = new FileAlterationMonitor(interval);
monitor.addObserver(observer);
monitor.start();
// 创建文件
File file = new File(testDir, "file.java");
FileUtils.touch(file);
ThreadUtils.sleep(Duration.ofSeconds(2));
// 更新文件
FileUtils.write(file, "testtest测试测试", Charset.defaultCharset());
ThreadUtils.sleep(Duration.ofSeconds(2));
// 删除文件
FileUtils.delete(file);
ThreadUtils.sleep(Duration.ofSeconds(2));
// 停止监控
monitor.stop();
NameFileComparator
:按文件名进行比较排序。PathFileComparator
:根据文件路径进行比较排序。SizeFileComparator
:按文件大小进行比较排序。LastModifiedFileComparator
:按文件的最后修改时间进行比较排序。ExtensionFileComparator
:按文件扩展名进行比较排序。final String testDir = "D://test";
Collection<File> listFiles = FileUtils.listFiles(new File(testDir), TrueFileFilter.INSTANCE, null);
// 使用不同的比较器进行排序
File[] sortedFiles;
// 按文件名排序(忽略大小写)
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, NameFileComparator.NAME_INSENSITIVE_COMPARATOR);
System.out.println("按文件名排序(忽略大小写):");
System.out.println(Arrays.toString(sortedFiles));
// 按文件路径排序
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, PathFileComparator.PATH_COMPARATOR);
System.out.println("按文件路径排序:");
System.out.println(Arrays.toString(sortedFiles));
// 按文件大小排序(从小到大)
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, SizeFileComparator.SIZE_COMPARATOR);
System.out.println("按文件大小排序(从小到大):");
System.out.println(Arrays.toString(sortedFiles));
// 按文件最后修改时间排序
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, LastModifiedFileComparator.LASTMODIFIED_COMPARATOR);
System.out.println("按文件最后修改时间排序:");
System.out.println(Arrays.toString(sortedFiles));
// 按文件扩展名排序
sortedFiles = listFiles.toArray(new File[0]);
Arrays.sort(sortedFiles, ExtensionFileComparator.EXTENSION_COMPARATOR);
System.out.println("按文件扩展名排序:");
System.out.println(Arrays.toString(sortedFiles));
FileFilter
: 用于过滤文件,可以根据文件的属性、名称等进行过滤。IOFileFilter
: 继承自 FileFilter
接口,提供更多的过滤方法来过滤文件和目录。TrueFileFilter
: IOFileFilter
的一个实现,始终返回 true,表示接受所有文件。FalseFileFilter
: IOFileFilter
的一个实现,始终返回 false,表示拒绝所有文件。SuffixFileFilter
: 根据文件后缀进行过滤。PrefixFileFilter
: 根据文件前缀进行过滤。WildcardFileFilter
: 使用通配符模式进行过滤。NameFileFilter
: 按文件名进行过滤。AndFileFilter
: 对多个过滤器使用逻辑与操作。OrFileFilter
: 对多个过滤器使用逻辑或操作。// 创建一个过滤器链,过滤出目录中符合条件的文件
IOFileFilter filter = FileFilterUtils.and(
FileFilterUtils.sizeFileFilter(100), // 大小超过100字节
FileFilterUtils.or(
FileFilterUtils.suffixFileFilter(".txt"), // 后缀为.txt
FileFilterUtils.prefixFileFilter("file") // 前缀为file
)
);
// 使用过滤器链来获取目录中符合条件的文件集合
Collection<File> filterFiles = FileUtils.listFiles(new File(testDir), filter, TrueFileFilter.INSTANCE);
// 打印过滤后的文件列表
System.out.println("文件过滤器");
for (File file : filterFiles) {
System.out.println(file.getAbsolutePath());
}
final String testDir = "D://test";
final String testFile = "D://test.java";
String extension = FilenameUtils.getExtension(testFile);
String baseName = FilenameUtils.getBaseName(testFile);
String name = FilenameUtils.getName(testFile);
System.out.println(String.format("%s, %s, %s", extension, baseName, name));
String normalize = FilenameUtils.normalize("C://test/../c/././d/e");
System.out.println(normalize); // C:\c\d\e
String normalize1 = FilenameUtils.normalize("../test/c");
System.out.println(normalize1); // null
String basePath = "C://test";
String filename = "file.java";
String concatFile = FilenameUtils.concat(basePath, filename);
System.out.println(concatFile); // C:\test\file.java
String directory = "C:\\a";
String child = "C:\\a\\test.java";
boolean contains = FilenameUtils.directoryContains(directory, child);
System.out.println(contains);
// *0或多个字符,?1个字符,
boolean match = FilenameUtils.wildcardMatch(child, "*.java");
System.out.println(match);
NullOutputStream
用于丢弃所有写入的数据,即相当于一个无操作的输出流使用,主要场景是在需要一个输出流对象,但实际上并不需要对数据进行任何的输出时,可以使用 NullOutputStream
来代替其他具体的输出流类,例如 FileOutputStream
或 ByteArrayOutputStream
。
使用 NullOutputStream
可以避免创建真实的输出流对象,从而节省资源和提高性能。同时,它还可以在某些情况下用作空操作的占位符,使代码逻辑更加清晰。
OutputStream outputStream = NullOutputStream.INSTANCE;
// 通过 NullOutputStream 对象写入数据
outputStream.write("Hello, world!".getBytes());
// 关闭输出流
outputStream.close();
CountingOutputStream
计算通过该流写入的字节数。每次写入数据时,CountingOutputStream
会自动更新内部的计数器。
通过使用 CountingOutputStream
,我们可以方便地计算输出了多少字节的数据,这在某些场景下可能是很有用的,比如需要统计文件的大小或者传输的数据量等。
// 创建一个输出流,用于写入数据
OutputStream outputStream = Files.newOutputStream(Paths.get("D://file.txt"));
// 创建一个 CountingOutputStream 对象,将其包装在原始的输出流上
CountingOutputStream countingOutputStream = new CountingOutputStream(outputStream);
// 写入数据到 CountingOutputStream
countingOutputStream.write("Hello, world!".getBytes());
// 获取计数器的值
long byteCount = countingOutputStream.getByteCount();
System.out.println(byteCount);
// 再次写入,获取累加值
countingOutputStream.write("中文字节数".getBytes());
byteCount = countingOutputStream.getByteCount();
System.out.println(byteCount);
// 关闭 CountingOutputStream(会自动关闭原始的输出流)
countingOutputStream.close();
DeferredFileOutputStream
将数据写入到内存中的缓冲区,并在达到指定阈值后,自动将数据写入到临时文件中。这种延迟写入的机制可以减少内存占用,特别适用于处理大量数据或大文件的场景。
String testString = "0123456789";
byte[] testBytes = testString.getBytes();
int initialBufferSize = 1024;
final String prefix = "commons-io-test";
final String suffix = ".out";
final Path tempDir = Paths.get("target");
File outFile = new File("testAboveThreshold.dat");
final DeferredFileOutputStream dfos = DeferredFileOutputStream.builder()
.setThreshold(testBytes.length - 5)
.setBufferSize(initialBufferSize)
.setPrefix(prefix)
.setSuffix(suffix)
.setDirectory(tempDir.toFile())
.setOutputFile(outFile)
.get();
dfos.write(testBytes, 0, testBytes.length);
dfos.close();
TeeOutputStream 可以将数据同时写入多个输出流中。这对于需要将数据同时写入到多个目标的情况非常有用,例如日志记录或数据备份等场景。
// 创建两个 FileOutputStream 对象,作为目标输出流
FileOutputStream outputStream1 = new FileOutputStream("file1.txt");
FileOutputStream outputStream2 = new FileOutputStream("file2.txt");
// 创建一个 TeeOutputStream 对象,将数据同时写入两个输出流
TeeOutputStream teeOutputStream = new TeeOutputStream(outputStream1, outputStream2);
// 写入数据到 TeeOutputStream
teeOutputStream.write("Hello, world!".getBytes());
// 关闭 TeeOutputStream
teeOutputStream.close();
// 关闭目标输出流
outputStream1.close();
outputStream2.close();
CloseShieldOutputStream
包装了一个底层的输出流,并提供了一种机制来屏蔽关闭操作。这意味着关闭 CloseShieldOutputStream
不会关闭底层的输出流。
// 创建一个 FileOutputStream 对象作为底层输出流
FileOutputStream outputStream = new FileOutputStream("file.txt");
// 创建一个 CloseShieldOutputStream 对象,将底层输出流包装起来
CloseShieldOutputStream closeShieldOutputStream = CloseShieldOutputStream.wrap(outputStream);
// 写入数据到 CloseShieldOutputStream
closeShieldOutputStream.write("Hello, world!".getBytes());
// 关闭 CloseShieldOutputStream(不会关闭底层输出流)
closeShieldOutputStream.close();
// 关闭底层输出流
outputStream.close();
在多数 Intel 架构的计算机上,使用小端字节顺序;而在一些网络协议中,常使用大端字节顺序。
double value = 3.14159;
// 将双精度浮点数的字节顺序进行交换,
double swappedValue = EndianUtils.swapDouble(value);
System.out.println(swappedValue); // 输出:2.5574005185030673E224
// 将小端序转化为大端序(Big-Endian)
// 将双精度浮点数 3.14159 写入字节数组
long value = Double.doubleToRawLongBits(3.14159);
bytes[7] = (byte) ((value >> 56) & 0xFF);
bytes[6] = (byte) ((value >> 48) & 0xFF);
bytes[5] = (byte) ((value >> 40) & 0xFF);
bytes[4] = (byte) ((value >> 32) & 0xFF);
bytes[3] = (byte) ((value >> 24) & 0xFF);
bytes[2] = (byte) ((value >> 16) & 0xFF);
bytes[1] = (byte) ((value >> 8) & 0xFF);
bytes[0] = (byte) (value & 0xFF);
// 从字节数组中读取双精度浮点数(交换字节顺序)
double result = EndianUtils.readSwappedDouble(bytes, 0);
System.out.println(result); // 输出: 3.14159
// 文件流字节序转换
EndianUtils.readSwappedInteger(Files.newInputStream(Paths.get("1.txt")));