備份是資料安全的最後防線。本文引導您從零打造一個功能強大的命令列壓縮備份工具 **ExpZip**,涵蓋遞迴掃描、正則過濾、ZIP 壓縮、校驗和註解、目錄結構保留、跨平台相容 — 全部使用 JDK 核心 API,零第三方依賴。
| 需求 | 技術實現 |
|---|---|
| 分散各目錄的檔案備份 | `File.listFiles()` 遞迴遍歷 |
| 複雜檔名模式匹配 (超越 DOS 通配符) | `FilenameFilter` + `Pattern` (Java 正則) |
| 依時間、大小、類型、修改日期過濾 | `File.lastModified()`、` |
| 壓縮並加入備份註解 | `ZipOutputStream`、` |
| 校驗值確保傳輸完整性 | `CheckedOutputStream` (Adler32/CRC32) |
| 保留相對目錄結構 | `ZipEntry` 名稱使用相對路徑 |
| 跨平台 (Windows/Mac/Linux) | 純 Java API、路徑分隔符用 `File.separator` |
File` 同時代表檔案與目錄路徑。listFiles()` 回傳子項目陣列;`listFiles(FilenameFilter)` 支援過濾。isFile()`、`isDirectory()`、`lastModified()`、`length()` 提供元資料判斷依據。
public interface FilenameFilter {
boolean accept(File dir, String name);
}
**策略模式精髓**:`listFiles()` 內部迴圈呼叫 `filter.accept(dir, name)`,開發者只需提供不同 `accept` 實作即可動態切換過濾邏輯,無須修改遍歷核心代碼。
class FolderFilter implements FilenameFilter {
public boolean accept(File dir, String name) {
return new File(dir, name).isDirectory(); // 使用 File(dir, name) 安全構建路徑
}
}
使用:`File[] subDirs = root.listFiles(new FolderFilter());`
Java 正則 (Perl 風格) 功能遠超 DOS 通配符。Java 檔名規則:字母/`$`/`_` 開頭,接字母/數字/`$`/`_`,副檔名 `.java` → 正則:
String regex = "[a-zA-Z_$][\\w$]*\\.java"; // \w = [a-zA-Z0-9_]
// 簡寫:"[a-zA-Z_$][\\w$]*\\.java"
通用 `FileFilter` 實作:
public class FileFilter implements FilenameFilter {
private final Pattern pattern;
public FileFilter(String regex) { this.pattern = Pattern.compile(regex); }
public boolean accept(File dir, String name) {
File f = new File(dir, name);
return f.isFile() && pattern.matcher(f.getName()).matches();
}
}
public class ExpZip {
public static void main(String[] args) {
if (args.length < 2) { usage(); return; }
File root = new File(args[0]);
String regex = args[1]; // 如 "[a-zA-Z_$][\\w$]*\\.java"
// 1. 遞迴收集符合條件的檔案
List files = new ArrayList<>();
collectFiles(root, new FileFilter(regex), files);
// 2. 建立 ZIP 輸出流 (含校驗)
try (FileOutputStream fos = new FileOutputStream("Backup.zip");
CheckedOutputStream cos = new CheckedOutputStream(fos, new Adler32());
ZipOutputStream zos = new ZipOutputStream(cos)) {
zos.setComment("ExpZip backup " + new Date() + " | " + files.size() + " files");
// 3. 逐個寫入 ZIP
for (File f : files) {
String entryName = getRelativePath(root, f); // 相對路徑
zos.putNextEntry(new ZipEntry(entryName));
Files.copy(f.toPath(), zos); // Java 7+ 高效複製
zos.closeEntry();
}
System.out.println("校驗值: " + cos.getChecksum().getValue());
}
}
static void collectFiles(File dir, FilenameFilter filter, List result) {
File[] children = dir.listFiles();
if (children == null) return;
for (File child : children) {
if (child.isDirectory()) {
collectFiles(child, filter, result); // 遞迴
} else if (filter.accept(dir, child.getName())) {
result.add(child);
}
}
}
static String getRelativePath(File root, File file) {
String absRoot = root.getAbsolutePath();
String absFile = file.getAbsolutePath();
return absFile.substring(absRoot.length() + 1).replace(File.separatorChar, '/');
}
}
# 備份 Eclipse 工作區所有 Java 檔
java ExpZip "C:\Users\Gary\workspace" "[a-zA-Z_$][\\w$]*\\.java"
# 備份 /home/user/docs 下所有 2024 年修改的 PDF
java ExpZip "/home/user/docs" ".*\\.pdf" # 需在程式中加入 lastModified 判斷
ExpZip 雖為教學範例,但其架構已具備生產級備份工具的核心基因:可組合的過濾策略、標準化的封存格式、內建完整性校驗、跨平台零依賴。