How to use ProgressMonitorInputStream

最后都变了- 提交于 2019-11-27 08:10:30

问题


I know I must be missing something very obvious, but whenever I try to use the ProgressMonitorInputStream when copying a file, I never get the ProgressDialog popup.

The examples I see don't seem to do much other than wrap their input stream within the ProgressMonitorInputStream.

The docs say:

This creates a progress monitor to monitor the progress of reading the input stream. If it's taking a while, a ProgressDialog will be popped up to inform the user. If the user hits the Cancel button an InterruptedIOException will be thrown on the next read. All the right cleanup is done when the stream is closed.

Here is an extremely simple example I put together that never pops up the dialog with a large file even if I setMillisToPopup() to an insanely small number.

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();
   }
}

Any suggestions?


回答1:


You are calling copyFile from within the context of the Event Dispatching Thread, this means the EDT is unable to respond to new events or paint requests until after the method returns.

Try placing the call within it's own Thread context...

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

Equally, you could use a SwingWorker, it's a little bit of overkill, but you get the benefit of the PropertyChangeListener or it's done method, which could be used to re-enable the JButton, should you want to prevent people from clicking the button while a copy operation is in progress

See Concurrency in Swing and Worker Threads and SwingWorker for more details

Updated with example

Copying a 371mb file, across the local disk...

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();
    }
}

Remember, there is overhead involved in setting up the window and displaying, which needs to be factored in. The system may "want" to display a window, but by the time the system has set it up and is prepared to display it, the steam may have finished copying...

Extended Example

nb: I don't really like the ProgressMonitor API as I've not been able to find where the UI is synchronised with the EDT, this can cause issues in Java 7 & 8

You could formalise the idea into a self contained worker, for example...

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) {
        }
    }

}

This attempts to contain the functionality, but also deals with the cleanup of the ProgressMonitorInputStream within the done method, making sure that it's done within the EDT. I'd personally attach a PropertyChangeListener to it and monitor the done property to determine when the worker has completed and examine the return result in order to pick up any exceptions, this gives you the ability to handle the exceptions in your own way and de-couples the worker from your process




回答2:


Your program works on files, but when it comes to server and client streams, it fails.

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());
        } 
    }
}


来源:https://stackoverflow.com/questions/26394714/how-to-use-progressmonitorinputstream

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!