torutkのブログ

ソフトウェア・エンジニアのブログ

java.net.URLの使い方とちょっとNIO.2のメモ

java.net.URLを使ってHTTP接続してファイルをダウンロードするプログラムを作っているときにはまったことのメモです。

getFile()メソッドは名前で安易に振る舞いを想像してはならない

以下のサンプルコードを実行すると、

public void test() throws IOException {
    URL url = new URL("http://www.example.com/foo/bar.zip?a=b");
    System.out.println(url.getFile());
    System.out.println(url.getPath());
}

次のように表示されます。

/foo/bar.zip?a=b
/foo/bar.zip

getFile()は、メソッド名から想像するとbar.zipを返すように思ってしまいましたが、実際は違います。Javadocで確認すると「getPath()にgetQuery()を連結したもの」とあります。

やりたかったことは、URLからファイル名部分(この例ではbar.zip)を抜き出すことです。URLクラスにはずばりのメソッドがないので文字列の切り出しを行う必要があります。

    String[] pathElements = url.getPath().split("/");
    String fileName = pathElements[pathElements.length - 1];

そんなとき、Java SE 7のNIO.2で追加されたjava.nio.file.Pathsを使うと

    Path path = Paths.get(url.getPath());
    String fileName = path.getFileName();

あるいは一行で

    String fileName = Paths.get(url.getPath()).getFileName();

と書くことができます。

URLからInputStreamの取り出し方

URLからInputStreamを取得するには、次の方法があります。

  • URLクラスのopenStream()メソッドでInputStreamを取得する
  • URLクラスのopenConnection()メソッドでURLConnectionを取得し、URLConnectionのgetInputStream()メソッドでInputStreamを取得する

前者が簡単ですが、タイムアウト設定やProxy設定をすることができないようです。また、HTTPのPOSTで取得する場合に対応できません。

URLConnectionは、AutoCloseableでない

URLConnectionは、使用を終了するときはdisconnect()を呼ぶのがよさそうですが(ステータスに応じて呼ぶ/呼ばないを使い分けるべきかも)、AutoCloseableインタフェースを実装していないので、Java SE 7で追加されたtry with resourceに対応していません。

URLのデータをローカルファイルに保存

URLから取り出したInputStreamを、ローカルファイルのOutputStreamにループでreadしてwriteするのかなと思ってましたが、NIO.2のFiles.copyメソッドを使うと

  try (BufferedInputStream in = new BufferedInputStream(con.getInputStream())) {
    Path savePath = FileSystems.getDefault().getPath("C:/tmp/bar.zip");
    Files.copy(in, savePath, StandardCopyOption.REPLACE_EXISTING);
  } catch (IOException ex) {
    ...
  }

といった感じで、InputStreamからPathへつなげることができるようです。