首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java Swing 文件複製進度條實現:多線程與 ProgressMonitor 完整教學

     編輯:Java 相關知識

前言

文件複製操作若無進度提示,用戶體驗極差。本文完整實現基於 Swing 的文件複製進度對話框,採用 ProgressMonitorInputStream 內建組件,結合多線程技術避免界面凍結,提供可直接整合的完整代碼。

架構設計

採用雙窗體架構:主窗體 UserMonitorFrame 負責用戶交互(選擇源文件/目標目錄、啟動複製),進度監控窗體 ProgressMonitorTest 封裝複製邏輯與進度顯示。複製操作在獨立線程執行,不阻塞 EDT (Event Dispatch Thread)。

進度監控核心類

ProgressMonitorInputStream 會自動監控輸入流讀取進度,當預估耗時超過 2 秒自動彈出進度對話框,支援用戶取消操作。

public class ProgressMonitorTest {
    public void useProgressMonitor(JFrame frame, String copyPath, String newPath) {
        try {
            File file = new File(copyPath);
            File newFile = new File(newPath);
            
            try (FileOutputStream fop = new FileOutputStream(newFile);
                 FileInputStream in = new FileInputStream(file);
                 ProgressMonitorInputStream pm = new ProgressMonitorInputStream(
                     frame, "文件讀取中,請稍後...", in)) {
                
                byte[] bytes = new byte[1024];
                int c;
                while ((c = pm.read(bytes)) != -1) {
                    fop.write(bytes, 0, c);
                }
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }
}

主窗體完整實現

實現 Runnable 接口,在 run() 方法中啟動複製任務。界面包含源文件選擇、目標目錄選擇、開始複製三個核心控件,使用 JFileChooser 實現文件/目錄選擇對話框。

public class UserMonitorFrame extends JFrame implements Runnable {
    private JPanel contentPane;
    private JTextField textField;      // 源文件路徑
    private JTextField textField_1;    // 目標目錄路徑
    private File file;

    public UserMonitorFrame() {
        setResizable(false);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setBounds(100, 100, 501, 184);
        setTitle("文件複製進度條演示");
        getContentPane().setLayout(null);

        // 源文件選擇區域
        JLabel label = new JLabel("文件地址:");
        label.setBounds(10, 10, 70, 15);
        getContentPane().add(label);

        textField = new JTextField();
        textField.setBounds(90, 7, 300, 21);
        getContentPane().add(textField);
        textField.setColumns(10);

        JButton btnSelectFile = new JButton("選擇文件");
        btnSelectFile.addActionListener(e -> doSelectFile());
        btnSelectFile.setBounds(400, 6, 93, 23);
        getContentPane().add(btnSelectFile);

        // 目標目錄選擇區域
        JLabel label_1 = new JLabel("複製地址:");
        label_1.setBounds(10, 40, 70, 15);
        getContentPane().add(label_1);

        textField_1 = new JTextField();
        textField_1.setBounds(90, 38, 300, 21);
        getContentPane().add(textField_1);
        textField_1.setColumns(10);

        JButton btnSelectDir = new JButton("選擇地址");
        btnSelectDir.addActionListener(e -> doSelectDir());
        btnSelectDir.setBounds(400, 39, 93, 23);
        getContentPane().add(btnSelectDir);

        // 開始複製按鈕
        JButton btnCopy = new JButton("開始複製");
        btnCopy.addActionListener(e -> doCopy());
        btnCopy.setBounds(182, 69, 93, 23);
        getContentPane().add(btnCopy);
    }

    // 選擇源文件
    protected void doSelectFile() {
        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            file = chooser.getSelectedFile();
            textField.setText(file.toString());
        }
    }

    // 選擇目標目錄
    protected void doSelectDir() {
        JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
        if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
            file = chooser.getSelectedFile();
            textField_1.setText(file.toString());
        }
    }

    // 啟動複製線程
    protected void doCopy() {
        new Thread(this).start();
    }

    @Override
    public void run() {
        ProgressMonitorTest test = new ProgressMonitorTest();
        String sourcePath = textField.getText();
        String targetDir = textField_1.getText();
        
        // 保持原文件名
        String fileName = sourcePath.substring(sourcePath.lastIndexOf(File.separator));
        String targetPath = targetDir + fileName;
        
        test.useProgressMonitor(this, sourcePath, targetPath);
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(() -> {
            try {
                UserMonitorFrame frame = new UserMonitorFrame();
                frame.setVisible(true);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
}

關鍵技術點深度解析

技術組件作用關鍵配置
ProgressMonitorInputStream自動進度監控與對話框顯示2秒延遲自動彈出、支援用戶取消
SwingUtilities.invokeLater確保 GUI 創建在 EDT 執行線程安全初始化
Runnable + Thread耗時操作移出 EDT防止界面凍結、支援取消響應
JFileChooser原生文件/目錄選擇對話框FILES_ONLY / DIRECTORIES_ONLY 模式
try-with-resources自動資源管理JDK 7+ 語法糖,保證流關閉

進階功能擴展建議

  • 速度顯示:計算實時複製速度 (MB/s) 並顯示在進度對話框
  • 剩餘時間估算:基於已複製量與耗時預估剩餘時間
  • 斷點續傳:記錄已複製位置,支援中斷後從斷點恢復
  • 多文件批量複製:隊列管理多文件任務,顯示總體與單文件雙層進度
  • 校驗整合:複製同步計算 MD5,完成後自動驗證完整性
  • 系統托盤集成:最小化到托盤繼續後台複製,彈出通知提醒完成

常見問題與解決方案

  1. 進度條不顯示:文件過小、複製太快(<2秒),ProgressMonitorInputStream 不會彈出對話框,屬正常行為
  2. 界面假死:未使用獨立線程執行複製任務,必須將耗時操作移出 EDT
  3. 目標文件名衝突:示例直接使用源文件名,生產環境需增加重名處理策略(覆蓋/重命名/跳過)
  4. 跨磁盤複製權限:確保目標目錄有寫入權限,捕獲 SecurityException 友好提示
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved