Catturare l'output di un comando in Java
Quando lanciamo un comando da un nostro programma in Java (Swing), di default non ne vediamo l'output.
E il motivo è che, generalmente, viene mandato sullo standard output, che non è la interfaccia scritta in Java.
In molti casi, però, potrebbe essere importante catturare l'output, soprattutto per vedere a che punto siamo con la sua esecuzione.
Oggi vediamo proprio questo: come visualizzare l'output di un comando su una JTextArea.
Il programma che lanceremo sarà youtube-dl, programma da riga di comando per scaricare la musica in formato MP3 da Youtube.
Nel nostro JFrame dovremmo avere almeno questi componenti:
- JTextField per immetere il link al video
- JTextArea per vedere l'output
- JButton per avviare il comando
Nel JFrame leghiamo un evento al JButton:
private Process p;
private StartYDL ydl;
btnStart.addActionListener((ActionEvent e) -> {
start();
});
........................................................................
private void start() {
ydl = new StartYDL(txtOutput, txtLink, btnStart);
if (StringUtils.isNotBlank(txtLink.getText())) {
try {
String cmd = "youtube-dl -x --audio-format mp3 -o " " + txtLink.getText();
p = Runtime.getRuntime().exec(cmd);
ydl.commence(p);
} catch (IOException ex) {
JOptionPane.showMessageDialog(this, ex.getMessage());
}
} else {
JOptionPane.showMessageDialog(this, "Inserire un link");
}
}
Qui facciamo un pò di controlli e poi lanciamo il comando con Runtime.
A questo punto vediamo la classe StartYDL:
public class StartYDL implements Runnable {
protected final JTextArea textArea;
protected final JTextField txtLink;
protected final JButton btn;
protected Reader reader = null;
private Thread thread;
public StartYDL(JTextArea textArea, JTextField txtLink, JButton btn) {
this.textArea = textArea;
this.txtLink = txtLink;
this.btn = btn;
}
public void commence(Process p) {
InputStream in = p.getInputStream();
reader = new InputStreamReader(in);
thread = new Thread(this);
thread.start();
}
@Override
public void run() {
StringBuilder sb = new StringBuilder();
btn.setEnabled(false);
txtLink.setEnabled(false);
try {
while (reader != null) {
int c = reader.read();
if (c == -1) {
return;
}
sb.append((char) c);
setText(sb.toString());
}
} catch (IOException ex) {
sb.append("nnERROR:n").append(ex.toString());
setText(sb.toString());
} finally {
try {
reader.close();
setText("");
setText("DOWNLOAD COMPLETATO");
setTextLink("");
} catch (IOException ex) {
sb.append(ex.getMessage());
}
}
}
public void stop() {
thread.interrupt();
setText("DOWNLOAD CANCELLATO");
setTextLink("");
}
private void setText(final String text) {
EventQueue.invokeLater(() -> {
textArea.setText(text);
});
}
private void setTextLink(final String text) {
EventQueue.invokeLater(() -> {
txtLink.setText(text);
btn.setEnabled(true);
txtLink.setEnabled(true);
txtLink.requestFocus();
});
}
}
Questa classe implementa Runnable, e lancia il programma in un altro Thread.
Al costruttore gli passiamo la JTextArea, la JTextField e il JButton, sui quali eseguiamo delle "operazioni" durante l'esecuzione del programma in background.
Ovviamente, questo esempio non è dei più basici, ma fa capire varie cose che possono essere importanti!
Potete usare il comando che volete, e anche i componenti grafici che vi servono.
Basterà modificare il comando lanciato con Runtime, e i parametri dati al csotruttore di StartYDL.
Infine, ci sono sicuramente modi migliori da un punto di vista "stilistico", però questo metodo è abbastanza veloce da mettere su; e non è sbagliato.
Enjoy!
java process runitime jframe jtextarea jtextfield jbutton thread runnable
Commentami!