TITLE:ProgressMonitorInputStreamを使用してテキストファイルのダウンロード状況を表示

Posted by at 2013-04-22

ProgressMonitorInputStreamを使用してテキストファイルのダウンロード状況を表示

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

  • &jnlp;
  • &jar;
  • &zip;
ProgressMonitorInputStream.png

サンプルコード

worker = new SwingWorker<String, Chunk>() {
  @Override public String doInBackground() {
    Charset cs = Charset.forName("EUC-JP");
    String ret = "Done";
    String path = "http://terai.xrea.jp/";
    URLConnection urlConnection;
    try{
      urlConnection = new URL(path).openConnection();
      System.out.println(urlConnection.getContentEncoding());
      System.out.println(urlConnection.getContentType());

      String encoding = urlConnection.getContentEncoding();
      if(encoding!=null) {
        cs = Charset.forName(encoding);
      }else{
        String contentType = urlConnection.getContentType();
        for(String value: contentType.split(";")) {
          value = value.trim();
          if(value.toLowerCase().startsWith("charset=")) {
            encoding = value.substring("charset=".length());
          }
        }
        if(encoding!=null) {
          cs = Charset.forName(encoding);
        }
      }
      System.out.println(cs);
    }catch(Exception ex) {
      ex.printStackTrace();
      ret = "Error";
      return ret;
    }
    int length = urlConnection.getContentLength();
    try(InputStream is = urlConnection.getInputStream();
        ProgressMonitorInputStream pmis = new ProgressMonitorInputStream(frame, "Loading", is);
        BufferedReader reader = new BufferedReader(new InputStreamReader(pmis, cs))) {

      monitor = pmis.getProgressMonitor();
      monitor.setNote(" "); //Need for JLabel#getPreferredSize
      monitor.setMillisToDecideToPopup(0);
      monitor.setMillisToPopup(0);
      monitor.setMinimum(0);
      monitor.setMaximum(length);

      int i = 0;
      int size = 0;
      String line;
      while((line = reader.readLine()) != null) {
        if(i++%50==0) { //Wait
          Thread.sleep(10);
        }
        size += line.getBytes(cs).length + 1; //+1: \n
        String note = String.format("%03d%% - %d/%d%n", 100*size/length, size, length);
        publish(new Chunk(line, note));
      }
    }catch(InterruptedException | IOException ex) {
      ret = "Exception";
      cancel(true);
    }
    return ret;
  }
  @Override protected void process(List<Chunk> chunks) {
    for(Chunk c: chunks) {
      textArea.append(c.line+"\n");
      monitor.setNote(c.note);
      //System.out.println(c.note);
    }
    textArea.setCaretPosition(textArea.getDocument().getLength());
  }
  @Override public void done() {
    frame.getGlassPane().setVisible(false);
    runButton.setEnabled(true);
    String text = null;
    try{
      text = isCancelled() ? "Cancelled" : get();
    }catch(Exception ex) {
      ex.printStackTrace();
      text = "Exception";
    }
    System.out.println(text);
  }
};
worker.execute();
View in GitHub: Java, Kotlin

解説

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

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

参考リンク

コメント