Apache Commons IO 是 Java 文件操作的標準庫,提供了線程安全、異常處理完善的高級 API。本文封裝一個生產就緒的 FileUtil 工具類,整合複製、刪除、字符串寫入、內容讀取、追加寫入等常用功能,並針對編碼、目錄自動創建、資源釋放進行增強。
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.15.1</version> <!-- 檢查最新版本 -->
</dependency>
package com.example.util;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.Charsets;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
public class FileUtil {
// ========== 1. 複製文件/目錄 ==========
/**
* 複製文件或目錄到目標文件夾
* @param srcPath 源路徑(文件或目錄)
* @param destDir 目標文件夾(必須存在或可創建)
*/
public static void copy(String srcPath, String destDir) throws IOException {
File src = new File(srcPath);
File dest = new File(destDir);
if (!dest.exists()) {
if (!dest.mkdirs()) throw new IOException("無法創建目標目錄:" + destDir);
}
if (src.isDirectory()) {
FileUtils.copyDirectoryToDirectory(src, dest);
} else if (src.isFile()) {
FileUtils.copyFileToDirectory(src, dest);
} else {
throw new FileNotFoundException("源路徑不存在:" + srcPath);
}
}
// ========== 2. 刪除文件/目錄 ==========
/**
* 遞歸刪除文件或目錄(目錄非空也可刪除)
*/
public static void delete(String path) throws IOException {
File target = new File(path);
if (target.isDirectory()) {
FileUtils.deleteDirectory(target);
} else if (target.isFile()) {
if (!target.delete()) {
throw new IOException("文件刪除失敗:" + path);
}
}
}
// ========== 3. 字符串寫入文件(自動創建父目錄) ==========
/**
* 將字符串寫入文件,指定編碼(默認 UTF-8)
* @return true 成功
*/
public static boolean writeString(String content, String filePath, String encoding) throws IOException {
File file = new File(filePath);
// 自動創建父目錄
File parent = file.getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs()) {
throw new IOException("無法創建父目錄:" + parent);
}
FileUtils.writeStringToFile(file, content, Charset.forName(encoding), false);
return true;
}
/** 默認 UTF-8 重載 */
public static boolean writeString(String content, String filePath) throws IOException {
return writeString(content, filePath, StandardCharsets.UTF_8.name());
}
// ========== 4. 讀取文件所有行 ==========
/**
* 讀取文件每行內容到 List,自動處理編碼
* @return 列表,文件不存在返回空列表
*/
public static List readLines(String filePath, String encoding) throws IOException {
File file = new File(filePath);
if (!file.exists()) return new ArrayList<>();
return FileUtils.readLines(file, Charset.forName(encoding));
}
/** 默認 UTF-8 重載 */
public static List readLines(String filePath) throws IOException {
return readLines(filePath, StandardCharsets.UTF_8.name());
}
// ========== 5. 追加內容到文件 ==========
/**
* 追加多行內容到文件末尾
*/
public static void appendLines(String filePath, List lines, String encoding) throws IOException {
File file = new File(filePath);
File parent = file.getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs()) {
throw new IOException("無法創建父目錄:" + parent);
}
FileUtils.writeLines(file, Charset.forName(encoding), lines, null, true); // true = append
}
/** 默認 UTF-8、換行符 \n 重載 */
public static void appendLines(String filePath, List lines) throws IOException {
appendLines(filePath, lines, StandardCharsets.UTF_8.name());
}
// ========== 6. 進階:大文件分行處理(避免 OOM) ==========
/**
* 逐行處理大文件,配合 LineProcessor 回調
*/
public static void processLargeFile(String filePath, LineProcessor processor) throws IOException {
File file = new File(filePath);
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
String line;
int lineNum = 0;
while ((line = reader.readLine()) != null) {
processor.process(lineNum++, line);
}
}
}
@FunctionalInterface
public interface LineProcessor {
void process(int lineNumber, String line) throws IOException;
}
// ========== 測試主方法 ==========
public static void main(String[] args) throws IOException {
String testDir = "D:/fileutil_test";
String testFile = testDir + "/demo.txt";
// 1. 寫入
writeString("第一行\n第二行\n第三行", testFile);
System.out.println("寫入完成");
// 2. 讀取
List lines = readLines(testFile);
System.out.println("讀取內容:" + lines);
// 3. 追加
appendLines(testFile, List.of("第四行", "第五行"));
System.out.println("追加完成,最新內容:" + readLines(testFile));
// 4. 複製
copy(testFile, testDir + "/backup");
System.out.println("複製完成");
// 5. 大文件處理示例
processLargeFile(testFile, (num, line) ->
System.out.printf("Line %d: %s%n", num, line));
// 6. 清理
delete(testDir);
System.out.println("測試目錄已清理");
}
}
所有讀寫方法強制要求 Charset 參數,默認提供 UTF-8 重載。避免使用平台默認編碼(FileReader 等)導致跨平台亂碼。
寫入/追加前檢查父目錄,不存在則 mkdirs(),消除 "No such file or directory" 異常。
內部使用 Commons IO 的 FileUtils,其底層已正確處理流關閉;大文件處理方法採用 try-with-resources 確保 BufferedReader 關閉。
提供 LineProcessor 函數式接口,支持流式逐行處理 GB 級文件,不載入內存。
| 問題 | 原因 | 解決 |
|---|---|---|
| 中文亂碼 | 未指定編碼,Windows 默認 GBK,Linux 默認 UTF-8 | 統一指定 StandardCharsets.UTF_8 |
| 大文件 OOM | readLines() 一次性載入所有行 | 使用 processLargeFile() 流式處理 |
| 追加覆蓋 | writeLines 最後參數 append 為 false | 調用 appendLines() 或傳 true |
| 複製失敗 | 目標目錄不存在 | 工具類內部自動 mkdirs() |
本工具類基於 Apache Commons IO 2.x 封裝,提供了線程安全、編碼安全、資源安全的文件操作 API。實際項目中可直接引入,並根據需要擴展:MD5 校驗、壓縮/解壓、文件監聽等功能。記住:永遠顯式指定 UTF-8 編碼,大文件必須流式處理。