概要

ProgressMonitorInputStreamを使用してテキストファイルのダウンロード状態を進捗表示します。

サンプルコード

class RunAction extends AbstractAction {
  public RunAction() {
    super("Load");
  }

  @Override public void actionPerformed(ActionEvent e) {
    runButton.setEnabled(false);
    textArea.setText("");
    URLConnection urlConnection = getURLConnection();
    if (urlConnection == null) {
      return;
    }
    Charset cs = getCharset(urlConnection, "UTF-8");
    int length = urlConnection.getContentLength();
    JFrame frame = (JFrame) SwingUtilities.getWindowAncestor(
        (Component) e.getSource());
    try {
      InputStream is = urlConnection.getInputStream();
      ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(
          frame, "Loading", is);
      monitor = pmis.getProgressMonitor();
      monitor.setNote(" "); //Need for JLabel#getPreferredSize
      monitor.setMillisToDecideToPopup(0);
      monitor.setMillisToPopup(0);
      monitor.setMinimum(0);
      monitor.setMaximum(length);
      worker = new MonitorTask(pmis, cs, length);
      worker.execute();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
  }
}

private class MonitorTask extends Task {
  public MonitorTask(ProgressMonitorInputStream pmis, Charset cs, int length) {
    super(pmis, cs, length);
  }

  @Override protected void process(List<Chunk> chunks) {
    for (Chunk c : chunks) {
      textArea.append(c.line + "\n");
      monitor.setNote(c.note);
    }
    textArea.setCaretPosition(textArea.getDocument().getLength());
  }

  @Override public void done() {
    runButton.setEnabled(true);
    String text = null;
    try {
      if (pmis != null) {
        pmis.close();
      }
      text = isCancelled() ? "Cancelled" : get();
    } catch (IOException | InterruptedException | ExecutionException ex) {
      ex.printStackTrace();
      text = "Exception";
    }
    System.out.println(text);
  }
}

private static class Task extends SwingWorker<String, Chunk> {
  protected final ProgressMonitorInputStream pmis;
  protected final Charset cs;
  protected final int length;
  public Task(ProgressMonitorInputStream pmis, Charset cs, int length) {
    super();
    this.pmis = pmis;
    this.cs = cs;
    this.length = length;
  }

  @Override public String doInBackground() {
    String ret = "Done";
    try (Scanner scanner = new Scanner(
          new BufferedReader(new InputStreamReader(pmis, cs)))) {
      int i = 0;
      int size = 0;
      while (scanner.hasNextLine()) {
        if (i % 50 == 0) { //Wait
          Thread.sleep(10);
        }
        i++;
        String line = scanner.nextLine();
        size += line.getBytes(cs).length + 1; //+1: \n
        String note = String.format(
            "%03d%% - %d/%d%n", 100 * size / length, size, length);
        //System.out.println(note);
        publish(new Chunk(line, note));
      }
    } catch (InterruptedException | IOException ex) {
      System.out.println("Exception");
      ret = "Exception";
      cancel(true);
    }
    return ret;
  }
}
View in GitHub: Java, Kotlin

解説

上記のサンプルでは、URLConnectionから開いたInputStreamProgressMonitorInputStreamをラップして、ファイルのダウンロード進捗状態をProgressMonitorで表示しています。

  • ProgressMonitorInputStreamの使用するProgressMonitorの最大値はファイルサイズ(バイト)
    • ProgressMonitorInputStreamがデフォルトで設定する最大値はInputStream#available()の値
    • この値がダウンロード中のストリームの合計バイト数を返す訳ではないので、これを最大値のままにしておくとProgressMonitorが表示されない場合がある
    • URLConnection#getContentLength()で取得したバイト数をProgressMonitor#setMaximum(...)で設定
  • 1行ずつJTextAreaに文字列として読み込ませるためにInputStreamReaderを使用しているので、エンコーディングをURLConnection#getContentEncoding()URLConnection#getContentType()などで取得
    • 何パーセント読み込んだかをProgressMonitor#setNote(...)で表示する場合は1行が何バイトかをString#getBytes(Charset)で取得して計算
    • このサンプルでは改行はLFのみの1バイトであると決め打ちしている
    • 進捗を表示する前にProgressMonitor#setNote("temp note");と適当な文字列を設定しておかないとNoteに使用するJLabelnullのままで表示されない、またはレイアウトがおかしくなる

参考リンク

コメント