1. はじめに
ファイルとネットワークの扱いは、プログラミングで非常に重要なテーマです。ほとんどの現代的なアプリは何らかの形でインターネットとやり取りし、データを取得したり、リクエストを送ったり、画像やドキュメントを受け取ったりします。チャットクライアントや音楽プレーヤーのような単純なプログラムでさえ、リソースのダウンロードを扱います。
今日は、Java でそれをどう行うかを、もっとも分かりやすく楽しい題材である画像を例に解説します。なぜ画像なのか?
- すぐに目に見える: うまく動けば、保存したファイルを開いて画像が見られます。結果が即時で分かりやすい。
- 汎用的なスキル: 画像は単なる画像ではなく、バイナリデータ、つまり「生のバイト」です。これに慣れれば、ドキュメント、動画、音楽、アーカイブなど何でもダウンロードできます。原理は同じです。
- 実用的: ネットワークからリソースをダウンロードする力は、壁紙ダウンローダーのようなシンプルなツールから、SNS の API を扱う高度なシステムまで、あらゆる現代アプリの土台になります.
ここでは2つのアプローチを扱います。手早い「郵便サービス」的な URL と、ヘッダー・ステータス・タイムアウトを制御できる「プロの運送会社」的な HttpClient です。
2. 最も簡単な方法: クラス URL
Java には URL クラスがあり、ネットワークアドレスを表します。これで接続を開き、データストリーム(InputStream 型)を取得できます。このストリームはファイルと同様に扱え、バイトを読み出したり、別のストリームにコピーしたり、テキストとして処理したりできます。
要するに、URL を使うのは、ブラウザのリンクからディスク上のファイルまでの最短ルートです。
最初の最小例
例えば、画像へのリンクがあるとします:
URL url = new URL("https://example.com/image.jpg");
Files.copy(url.openStream(), Path.of("a.jpg"));
たった2行です。何をしているのでしょうか?
- URL オブジェクトを作成し、画像のアドレス文字列を渡します。
- openStream() メソッドを呼び出してネットワーク接続を開き、InputStream を返します。
- Files.copy を使って、ストリームの内容をファイル "a.jpg" にコピーします。
結果: インターネット上の画像が作業フォルダに保存されます。
transferTo を使う方法
ストリームからファイルへデータを渡す別の方法もあります:
InputStream in = new URL("https://example.com/image.jpg").openStream();
in.transferTo(Files.newOutputStream(Path.of("b.jpg")));
ここでは、in ストリームの全データを transferTo メソッドで、ファイル "b.jpg" を書き出すストリームへ転送するよう明示しています。
結果は同じです: 画像がディスクに保存されます。
重要なポイント
この方法はとても簡単ですが注意点があります。まず、リンクから届いたものが何かをまったく検証していません。画像かもしれませんし、エラー 404 の HTML ページかもしれません。いずれの場合もファイルは保存され、違いが分かるのは画像ビューアで開こうとしたときです。
3. 現代的な方法: HttpClient
Java 11 から標準ライブラリに新しいツール — HttpClient — が加わりました。GET、POST などのメソッドを扱い、タイムアウトを管理し、ヘッダーとステータスを確認し、リダイレクトを処理できます。
画像のダウンロードでは特に有用で、実際にファイルが正常に取得できたか(エラーではないか)を確認できます。
HttpClient の最小例
URI uri = URI.create("https://example.com/image.jpg"); // URI は URL のより現代的な版
HttpClient client = HttpClient.newHttpClient(); // HttpClient オブジェクトを作成
HttpRequest request = HttpRequest.newBuilder(uri).build(); // "リクエスト" オブジェクトを作成
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("c.jpg"), response.body());
手順:
- HttpClient クライアントを作成します。
- 指定したアドレスに対する HttpRequest を構築します。
- リクエストを送信し、応答をバイト配列として受け取ります: HttpResponse.BodyHandlers.ofByteArray()。
- Files.write でこれらのバイトをファイル "c.jpg" に書き込みます。
結果は同じく画像が得られますが、サーバーがどのように応答したかについてより多くの情報が得られます。
ステータスコードの確認
response には statusCode() というメソッドがあります。これでサーバーが成功応答を返したか確認できます:
if (response.statusCode() == 200)
{
Files.write(Path.of("ok.jpg"), response.body());
}
else
{
System.out.println("エラー: コード " + response.statusCode());
}
これで、エラーページではなく、期待どおりのものを保存したと確信できます。
ヘッダーの取得
たとえば、コンテンツタイプをチェックしたいとします:
String type = response.headers().firstValue("Content-Type").orElse("不明");
System.out.println("コンテンツタイプ: " + type);
サーバーが "image/png" や "image/jpeg" を返したなら、それは本当に画像です。もし "text/html" が来たなら要注意です。
タイムアウト
サーバーの応答が遅くてもプログラムが固まらないよう、制限を設定できます:
URI uri = URI.create("https://example.com/image.jpg");
HttpRequest req = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(5))
.build();
サーバーが 5 秒以内に応答しない場合、リクエストはエラーで終了します。
リダイレクトの処理
リンクが直接ファイルではなく、まずリダイレクトページに導くことがあります。自動追跡を有効にするには次のとおりです:
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
4. 実践シナリオ
複数の画像を連続でダウンロード
1 枚ではなく数十、数百枚の画像をダウンロードしたいことはよくあります。たとえば、ユーザーのアバターや商品の写真を保存するプログラムを書く場合です。HttpClient を使えばループで簡単に実装できます:
var client = HttpClient.newHttpClient();
String[] urls = {
"https://example.com/img1.jpg",
"https://example.com/img2.jpg"
};
for (int i = 0; i < urls.length; i++)
{
var uri = URI.create(urls[i]);
var request = HttpRequest.newBuilder(uri).build();
var response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
if (response.statusCode() == 200) {
Files.write(Path.of("img" + i + ".jpg"), response.body());
}
}
ファイルサイズの確認
画像のバイト数を知りたいこともあります。サーバーは "Content-Length" ヘッダーで知らせてくれることがあります:
String length = response.headers().firstValue("Content-Length").orElse("?");
System.out.println("サイズ: " + length + " バイト");
ヘッダーがない場合は、response.body().length を見れば十分です。
ダウンロードしてプログラム内で表示
画像をダウンロードするだけでなく、その場で表示したいこともあります。javax.imageio.ImageIO ライブラリを使えます:
InputStream in = new URL("https://example.com/pic.png").openStream();
BufferedImage img = ImageIO.read(in);
System.out.println("幅: " + img.getWidth() + "、高さ: " + img.getHeight());
このように、ディスクに保存せずにメモリ上で画像をすぐ検証できます。
GO TO FULL VERSION