Как использовать ProgressMonitorInputStream

Я знаю, что мне не хватает чего-то очень очевидного, но всякий раз, когда я пытаюсь использовать ProgressMonitorInputStream при копировании файла, я никогда не получаю всплывающее окно ProgressDialog.

Примеры, которые я вижу, похоже, не делают ничего, кроме как оборачивают их входной поток в ProgressMonitorInputStream.

Документы говорят:

Это создает монитор прогресса для отслеживания прогресса чтения входного потока. Если это займет некоторое время, появится ProgressDialog, чтобы проинформировать пользователя. Если пользователь нажимает кнопку "Отмена", при следующем чтении будет выдано исключение InterruptedIOException. Вся правильная очистка выполняется, когда поток закрыт.

Вот чрезвычайно простой пример, который я собрал, который никогда не выдает диалоговое окно с большим файлом, даже если я setMillisToPopup() к безумно маленькому числу.

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.SwingWorker;

public class ProgressBarDemo extends JFrame {

   private static final long serialVersionUID = 1L;

   private JButton button;

   ProgressBarDemo() 
   {
      button = new JButton("Click me!");
      ButtonActionListener bal = new ButtonActionListener();
      button.addActionListener(bal);

      this.getContentPane().add(button);
   }

   private class ButtonActionListener implements ActionListener 
   {

      @Override
      public void actionPerformed(ActionEvent e) {
         // TODO Auto-generated method stub
         Worker worker = new Worker();
         worker.execute();
         button.setEnabled(false);
      }
   }

   public void go() {

      this.setLocationRelativeTo(null);
      this.setVisible(true);
      this.pack();
      this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
   }

   private class Worker extends SwingWorker<Void, Void>
   {

      private void copyFile() {

         File file = new File("/Users/mypath/Desktop/WirelessDiagnostics.tar.gz");

         BufferedInputStream bis;
         BufferedOutputStream baos;
         try {
            bis = new BufferedInputStream(new FileInputStream(file));
            ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(
                  ProgressBarDemo.this,
                  "Reading... " + file.getAbsolutePath(),
                  bis);

            pmis.getProgressMonitor().setMillisToPopup(10);
            baos = new BufferedOutputStream(new FileOutputStream("/Users/mypath/Desktop/NewWirelessDiagnostics.tar.gz"));

            byte[] buffer = new byte[2048];
            int nRead = 0;

            while((nRead = pmis.read(buffer)) != -1) {
               baos.write(buffer, 0, nRead);
            }

            pmis.close();
            baos.flush();
            baos.close();
         } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
         }
      }

      @Override
      protected Void doInBackground() throws Exception {
         // TODO Auto-generated method stub
         copyFile();
         return null;
      }

      @Override
      protected void done() {
         button.setEnabled(true);
      }
   }
}

public class TestProj {

   public static void main(String[] args) {
      ProgressBarDemo demo = new ProgressBarDemo();
      demo.go();
   }
}

Какие-либо предложения?

2 ответа

Решение

Ты звонишь copyFile в контексте потока диспетчеризации событий это означает, что EDT не может отвечать на новые события или запросы рисования до тех пор, пока метод не вернется.

Попробуйте сделать звонок внутри своего Thread контекст...

Thread t = new Thread(new Runnable(
    public void run() {
        copyFile();
    }
));
t.start();

Точно так же вы могли бы использовать SwingWorker Это немного излишне, но вы получаете выгоду от PropertyChangeListener или это done метод, который может быть использован для повторного включения JButton, если вы хотите, чтобы люди не нажимали кнопку во время операции копирования

См. Параллелизм в Swing и рабочих потоках и SwingWorker для получения более подробной информации

Обновлено с примером

Копирование файла 371 Мб на локальный диск...

копия

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.SwingWorker;

public class ProgressBarDemo extends JFrame {

    private static final long serialVersionUID = 1L;

    private JButton button;

    ProgressBarDemo() {
        button = new JButton("Click me!");
        ButtonActionListener bal = new ButtonActionListener();
        button.addActionListener(bal);

        this.getContentPane().add(button);
    }

    public void go() {

        this.setLocationRelativeTo(null);
        this.setVisible(true);
        this.pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    private void copyFile() {

        File file = new File("...");

        BufferedInputStream bis;
        BufferedOutputStream baos;
        try {
            bis = new BufferedInputStream(new FileInputStream(file));
            ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(
                            this,
                            "Reading... " + file.getAbsolutePath(),
                            bis);

            pmis.getProgressMonitor().setMillisToPopup(10);
            baos = new BufferedOutputStream(new FileOutputStream("..."));

            byte[] buffer = new byte[2048];
            int nRead = 0;

            while ((nRead = pmis.read(buffer)) != -1) {
                baos.write(buffer, 0, nRead);
            }

            pmis.close();
            baos.flush();
            baos.close();
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    private class ButtonActionListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {

            button.setEnabled(false);
            SwingWorker worker = new SwingWorker() {

                @Override
                protected Object doInBackground() throws Exception {
                    copyFile();
                    return null;
                }

                @Override
                protected void done() {
                    button.setEnabled(true);
                }

            };

            worker.execute();
        }

    }

    public static void main(String[] args) {
        ProgressBarDemo demo = new ProgressBarDemo();
        demo.go();
    }
}

Помните, что при настройке окна и отображении возникают накладные расходы, которые должны быть учтены. Система может "захотеть" отобразить окно, но к тому времени, когда система настроит его и будет готова отобразить его, Steam, возможно, закончил копирование...

Расширенный пример

нб: мне не очень нравится ProgressMonitor API, так как я не смог найти, где пользовательский интерфейс синхронизирован с EDT, это может вызвать проблемы в Java 7 и 8

Вы могли бы формализовать идею в автономного работника, например...

public class CopyWorker extends SwingWorker {

    private File source;
    private File dest;
    private Component parent;

    private ProgressMonitorInputStream pmis;

    public CopyWorker(Component parent, File source, File dest) {
        this.parent = parent;
        this.source = source;
        this.dest = dest;
    }

    @Override
    protected Object doInBackground() throws Exception {
        try (InputStream is = new FileInputStream(source)) {
            try (OutputStream os = new FileOutputStream(dest)) {
                pmis = new ProgressMonitorInputStream(
                        parent,
                        "Copying...",
                        is);

                pmis.getProgressMonitor().setMillisToPopup(10);

                byte[] buffer = new byte[2048];
                int nRead = 0;

                while ((nRead = pmis.read(buffer)) != -1) {
                    os.write(buffer, 0, nRead);
                }
            }
        }
        return null;
    }

    @Override
    protected void done() {
        try {
            pmis.close();
        } catch (Exception e) {
        }
    }

}

Это пытается сдержать функциональность, но также имеет дело с очисткой ProgressMonitorInputStream в пределах done метод, убедившись, что это сделано в рамках EDT. Я бы лично приложил PropertyChangeListener к нему и контролировать done свойство, позволяющее определить, когда работник завершил работу, и проверить результат возврата, чтобы выбрать любые исключения, это дает вам возможность обрабатывать исключения по-своему и отделять работника от вашего процесса.

Ваша программа работает с файлами, но когда дело доходит до потоков сервера и клиента, происходит сбой.

import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.Socket;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.ProgressMonitorInputStream;
import javax.swing.SwingWorker;

public class FileReceive extends JFrame {
    private static final long serialVersionUID = 1L;
    private BufferedInputStream bufferInput;
    private FileOutputStream fout;
    private BufferedOutputStream bufferOutput;
    private Socket client;
    private JButton button;
    private File fileinfo;
    ProgressMonitorInputStream pois;

    FileReceive() {
        setLocationRelativeTo(null);
        setVisible(true);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        pack();
        receiveFile();
    }

    public static void main(String arg[]) {
        new FileReceive();
    }

    public void receiveFile() {
        try {
            fileinfo=new File(filepath);
            client=new Socket("localhost",9090);
            fout=new FileOutputStream(fileinfo);
            bufferOutput=new BufferedOutputStream(fout);
            bufferInput=new BufferedInputStream(client.getInputStream());
            pois=new ProgressMonitorInputStream(this, "reading", bufferInput);
            int ch;
            byte[] b=new byte[2048];
            System.out.println("Receiving File");

            while(-1!=(ch=pois.read(b))) {
                bufferOutput.write(b,0,ch);
            }

            pois.close();
            bufferInput.close();
            bufferOutput.close();
            fout.close();
            client.close();
        } catch (IOException e) {
            JOptionPane.showMessageDialog(null, e.getMessage());
        } 
    }
}
Другие вопросы по тегам