Как использовать 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());
}
}
}