大檔案複製若無進度回饋,使用者體驗極差。Swing 提供 ProgressMonitorInputStream 內建進度監視對話框,配合多執行緒避免 UI 凍結,即可實現專業級複製體驗。本範例完整展示「選檔案 → 選目標資料夾 → 開啟新執行緒複製 → 自動彈出進度條」全流程。
// 進度監視邏輯封裝
public class ProgressMonitorTest {
public void useProgressMonitor(JFrame parent, String srcPath, String destPath) { ... }
}
// 主視窗:實作 Runnable,負責 UI 與啟動複製執行緒
public class UserMonitorFrame extends JFrame implements Runnable {
private JTextField textField; // 來源檔案
private JTextField textField_1; // 目標資料夾
...
}
public void useProgressMonitor(JFrame frame, String copyPath, String newPath) {
try {
File src = new File(copyPath);
File dest = new File(newPath);
try (FileInputStream fis = new FileInputStream(src);
FileOutputStream fos = new FileOutputStream(dest);
// 關鍵:包裝輸入流,超過 2 秒自動彈出進度對話框
ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(
frame, "文件讀取中,請稍後...", fis)) {
byte[] buf = new byte[1024];
int len;
while ((len = pmis.read(buf)) != -1) {
fos.write(buf, 0, len);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
機制:ProgressMonitorInputStream內部計時,若預估總耗時 > 2 秒(預設閾值),自動顯示JOptionPane風格進度對話框,含進度列、已讀取位元組、預估剩餘時間、取消按鈕。
JTextField + 「選擇檔案」按鈕(JFileChooser.FILES_ONLY)。JTextField + 「選擇資料夾」按鈕(JFileChooser.DIRECTORIES_ONLY)。new Thread(this).start()。// 按鈕事件
protected void do_copyButton_actionPerformed(ActionEvent e) {
new Thread(this).start(); // 關鍵:開啟新執行緒,避免阻塞 EDT
}
@Override
public void run() {
ProgressMonitorTest test = new ProgressMonitorTest();
String src = textField.getText();
String destDir = textField_1.getText();
// 目標檔案 = 目標資料夾 + 原檔名(含副檔名)
String destFile = destDir + src.substring(src.lastIndexOf("."));
test.useProgressMonitor(this, src, destFile);
}
Swing 是單執行緒模型(Event Dispatch Thread, EDT)。若在 EDT 執行耗時 I/O:
ProgressMonitorInputStream 內部計時器也跑在 EDT,導致進度條根本不會顯示或顯示後立即卡死。解法:new Thread(runnable).start() 將 I/O 移出 EDT,EDT 保持響應,進度對話框正常運作。
| 功能 | 實現方式 |
|---|---|
| 取消複製 | 輪詢 pmis.getProgressMonitor().isCanceled(),中斷迴圈並刪除半成品檔 |
| 進度百分比精確 | 預先 src.length() 取總大小,寫入時累加 bytesWritten,手動 monitor.setProgress() |
| 多檔案批次複製 | 外層迴圈遍歷檔案清單,內層複製單檔,總進度 = (已完成檔案大小 + 當前檔案進度) / 總大小 |
| 速度顯示 | System.nanoTime() 計算位元組/秒,格式化為 MB/s |
| 異常處理友善化 | 捕獲 IOException,用 SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog(...)) 回 EDT 顯示錯誤 |
| 現代化替代 | JavaFX Task + ProgressBar、Swing SwingWorker + 自定義 JDialog 進度條 |
// ProgressMonitorTest.java
package cn.edu.xidian.crytoll;
import java.io.*;
import javax.swing.*;
public class ProgressMonitorTest {
public void useProgressMonitor(JFrame frame, String copyPath, String newPath) {
// ... 如上所示
}
}
// UserMonitorFrame.java
package cn.edu.xidian.crytoll;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class UserMonitorFrame extends JFrame implements Runnable {
// ... UI 初始化、檔案選擇器、按鈕事件、run() 方法 ...
}
ProgressMonitorInputStream 是 Swing 內建的「零配置」進度條方案,適合中小檔案快速整合。SwingWorker(自帶 publish/process 進度回調)或 JavaFX Task,獲得更靈活的控制與現代化 UI。