Windows 原生僅支持單個文件重命名,批量重命名需借助第三方工具或腳本。本文實現一個基於 Swing 的圖形化批量重命名工具,核心功能包括:
# 號位符表示流水號)採用 GridBagLayout 實現靈活佈局,主要組件:
| 組件 | 用途 |
|---|---|
| JTextField (forderField) | 顯示選中的文件夾路徑 |
| JButton (瀏覽按鈕) | 彈出 JFileChooser 選擇目錄 |
| JTextField (templetField) | 輸入命名模板,如 photo### |
| JSpinner (startSpinner) | 設置起始編號 |
| JTextField (extNameField) | 輸入目標擴展名(如 jpg) |
| JTable | 展示「舊文件名」與「新文件名」對照 |
| JProgressBar | 處理大量文件時顯示進度 |
private static class ExtNameFileFilter implements FileFilter {
private final String extName;
public ExtNameFileFilter(String extName) {
this.extName = extName.toLowerCase();
if (!this.extName.startsWith(".")) this.extName = "." + this.extName;
}
@Override
public boolean accept(File pathname) {
return pathname.getName().toLowerCase().endsWith(extName);
}
}
protected void do_button_actionPerformed(ActionEvent e) {
JFileChooser chooser = new JFileChooser();
chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
chooser.setDialogTitle("選擇包含目標文件的文件夾");
if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
dir = chooser.getSelectedFile();
forderField.setText(dir.getAbsolutePath());
}
}
protected void do_startButton_actionPerformed(ActionEvent e) {
String templet = templetField.getText().trim();
if (templet.isEmpty()) {
JOptionPane.showMessageDialog(this, "請輸入重命名模板", "提示", JOptionPane.WARNING_MESSAGE);
return;
}
// 解析模板中的 # 號位符
int hashIndex = templet.indexOf('#');
if (hashIndex == -1) {
JOptionPane.showMessageDialog(this, "模板必須包含至少一個 # 號作為編號位符", "錯誤", JOptionPane.ERROR_MESSAGE);
return;
}
String placeholder = templet.substring(hashIndex); // 如 "###"
String formatPattern = templet.replace(placeholder, "%0" + placeholder.length() + "d"); // "photo%03d"
// 處理擴展名
String ext = extNameField.getText().trim().toLowerCase();
if (!ext.isEmpty() && !ext.startsWith(".")) ext = "." + ext;
// 獲取文件列表
File[] files = dir.listFiles(new ExtNameFileFilter(ext));
if (files == null || files.length == 0) {
JOptionPane.showMessageDialog(this, "未找到匹配的文件", "提示", JOptionPane.INFORMATION_MESSAGE);
return;
}
// 表格模型準備
DefaultTableModel model = (DefaultTableModel) table.getModel();
model.setRowCount(0);
int counter = (Integer) startSpinner.getValue();
// 執行重命名
for (File file : files) {
String newName = String.format(formatPattern, counter++) + ext;
model.addRow(new Object[]{file.getName(), newName});
File newFile = new File(file.getParent(), newName);
boolean success = file.renameTo(newFile);
if (!success) {
System.err.println("重命名失敗: " + file.getName() + " -> " + newName);
}
}
JOptionPane.showMessageDialog(this, "完成!共處理 " + files.length + " 個文件", "完成", JOptionPane.INFORMATION_MESSAGE);
}
模板中引入 * 符號代表原文件名(不含擴展名):
// 模板 "backup_*_###" -> 文件 "photo.jpg" -> "backup_photo_001.jpg"
String template = templetField.getText();
if (template.contains("*")) {
String baseName = FilenameUtils.getBaseName(file.getName());
template = template.replace("*", baseName);
}
// 記錄重命名操作歷史
List history = new ArrayList<>();
class RenameRecord {
File original;
File renamed;
long timestamp;
}
// 撤銷方法
public void undoLast() {
if (!history.isEmpty()) {
RenameRecord last = history.remove(history.size() - 1);
last.renamed.renameTo(last.original);
refreshTable();
}
}
// 使用 SwingWorker 避免界面凍結
new SwingWorker() {
@Override
protected Void doInBackground() {
for (File file : files) {
// 重命名邏輯...
publish(new FileRenameResult(oldName, newName, success));
}
return null;
}
@Override
protected void process(List chunks) {
// 更新表格和進度條
}
@Override
protected void done() {
// 完成提示
}
}.execute();
| 現象 | 可能原因 | 解決方案 |
|---|---|---|
| renameTo 返回 false | 目標文件已存在、權限不足、跨文件系統 | 檢查目標是否存在,嘗試 Files.move(REPLACE_EXISTING) |
| 文件順序不符合預期 | listFiles() 不保證順序 | 對文件數組按名稱/時間排序後再處理 |
| 中文文件名亂碼 | 編碼不一致 | 確保 JVM 文件編碼設置正確 (-Dfile.encoding=UTF-8) |
本工具展示了 Swing 構建實用桌面應用的完整流程:界面佈局、文件操作、字符串模板解析、表格數據綁定。核心重命名邏輯利用 String.format 處理編號格式化,配合 File.renameTo 實現原子重命名。生產級應用可進一步加入異步處理、撤銷機制、日誌記錄等健壯性功能。