

遠端視窗核心類別 FtpPanel 繼承 JPanel,負責 FTP 伺服器檔案列表顯示、目錄導航、檔案操作(刪除、重命名、建立資料夾、下載)、狀態列顯示。
FtpPanel
├── JToolBar 工具列
│ ├── 刪除按鈕 (delAction)
│ ├── 重命名按鈕 (renameAction)
│ ├── 新建資料夾按鈕 (createFolderAction)
│ ├── 下載按鈕 (downAction)
│ └── 重新整理按鈕 (refreshAction)
├── JScrollPane + JTable (ftpDiskTable) // 檔案列表表格
│ ├── 自訂 CellRenderer (FTPTableCellRanderer) // 圖示顯示
│ ├── TableRowSorter + TableStringConverter // 排序支援
│ └── 滑鼠雙擊事件處理 (目錄進入)
└── JLabel (ftpSelFilePathLabel) // 當前選中檔案完整路徑
// 自訂表格模型
ftpDiskTable.setModel(new FtpTableModel());
// 圖示渲染器:區分資料夾/檔案圖示
ftpDiskTable.getColumnModel().getColumn(0).setCellRenderer(
FTPTableCellRanderer.getCellRanderer());
// 排序器:支援欄位點擊排序
TableRowSorter sorter = new TableRowSorter<>(ftpDiskTable.getModel());
TableStringConverter converter = new TableConverter();
sorter.setStringConverter(converter);
ftpDiskTable.setRowSorter(sorter);
sorter.toggleSortOrder(0); // 預設按名稱排序
listFtpFiles(TelnetInputStream list) 方法解析伺服器回傳的原始目錄串流。支援兩種格式:
// 典型 Unix ls -l 格式:
-rwx------ 1 user group 57344 Apr 18 05:32 file.xls
drwx------ 1 user group 4096 Apr 16 21:46 folder_name
\n (10),Binary 模式 \r (13)\\s+ 分割多空白d=目錄, -=檔案// 核心解析迴圈片段
while (i < bufsize) {
if (names[i] == 13) { // Binary 模式換行
String fileMessage = new String(names, j, i - j);
if (!fileMessage.split("\\s+")[8].equals(".")
&& !fileMessage.split("\\s+")[8].equals("..")) {
String sizeOrDir = fileMessage.startsWith("d") ? ""
: fileMessage.split("\\s+")[4];
String fileName = fileMessage.split("\\s+")[8];
String dateStr = fileMessage.split("\\s+")[5] + " "
+ fileMessage.split("\\s+")[6] + " "
+ fileMessage.split("\\s+")[7];
FtpFile ftpFile = new FtpFile();
ftpFile.setLastDate(dateStr);
ftpFile.setSize(sizeOrDir);
ftpFile.setName(fileName);
ftpFile.setPath(pwd);
model.addRow(new Object[]{ftpFile, ftpFile.getSize(), dateStr});
}
j = i + 2; // 跳過 \r\n
}
i++;
}
private void ftpDiskTableMouseClicked(MouseEvent evt) {
int selectedRow = ftpDiskTable.getSelectedRow();
Object value = ftpDiskTable.getValueAt(selectedRow, 0);
if (value instanceof FtpFile) {
FtpFile selFile = (FtpFile) value;
ftpSelFilePathLabel.setText(selFile.getAbsolutePath());
if (evt.getClickCount() >= 2 && selFile.isDirectory()) {
try {
ftpClient.cd(selFile.getAbsolutePath());
listFtpFiles(ftpClient.list());
} catch (IOException ex) { ex.printStackTrace(); }
}
}
}
public void setFtpClient(FtpClient ftpClient) {
this.ftpClient = ftpClient;
// 30秒心跳保活
Timer timer = new Timer(3000, e -> {
try {
if (ftpClient != null && ftpClient.serverIsOpen()) {
ftpClient.noop(); // NOOP 指令保持連線
}
} catch (IOException ex) { ex.printStackTrace(); }
});
timer.start();
startDownThread(); // 啟動下載隊列執行緒
}
public void refreshCurrentFolder() {
try {
TelnetInputStream list = ftpClient.list();
listFtpFiles(list);
} catch (IOException e) { e.printStackTrace(); }
}
public void clearTable() {
FtpTableModel model = (FtpTableModel) ftpDiskTable.getModel();
model.setRowCount(0);
}
| 亮點 | 說明 |
|---|---|
| ActionMap 統一動作管理 | 按鈕動作集中定義,易於擴展國際化、快捷鍵綁定 |
| SwingUtilities 執行緒安全 | UI 更新正確委派給 EDT,避免併發問題 |
| TableRowSorter 內建排序 | 無需手寫排序邏輯,支援多欄位複合排序 |
| 心跳機制防斷線 | 長連線場景下自動發送 NOOP 維持控制通道 |
| 改進建議 | 說明 |
|---|---|
| 目錄解析器抽象化 | 支援 Windows (MS-DOS)、VMS、MVS 等不同 FTP 伺服器列表格式 |
| 編碼自動偵測 | 目前假設平台預設編碼,跨語言環境易亂碼 |
| 非同步載入提示 | 大目錄載入時顯示進度指示器,提升 UX |
| 單元測試覆蓋 | 解析邏輯複雜,需針對各格式編寫測試用例 |
FtpPanel 實現了功能完整的遠端檔案瀏覽元件,核心難點在於 FTP 列表格式的健壯解析與 Swing 執行緒模型的正確應用。實務專案建議引入 Apache Commons Net 或 JSch 等成熟庫替代手寫協定解析,專注於業務邏輯與 UI 體驗優化。