1. Einleitung
Die Arbeit mit Dateien und Netzwerken ist eines der wichtigsten Themen in der Programmierung. Fast jede moderne Anwendung interagiert auf die eine oder andere Weise mit dem Internet: Sie lädt Daten herunter, sendet Anfragen, empfängt Bilder oder Dokumente. Selbst ein einfaches Programm wie ein Chat-Client oder ein Musikplayer hat zwingend mit dem Laden von Ressourcen zu tun.
Heute schauen wir uns an, wie das in Java funktioniert, am Beispiel der anschaulichsten und angenehmsten Fracht – Bildern. Warum gerade Bilder?
- Sofort sichtbar: Wenn alles funktioniert hat, öffnen Sie die heruntergeladene Datei und sehen ein Bild. Das ist ein sofortiges und verständliches Ergebnis.
- Universelle Fähigkeit: Bilder sind nicht nur Bilder, sondern binäre Daten, „rohe Bytes“. Wenn Sie gelernt haben, damit zu arbeiten, können Sie alles Mögliche herunterladen: Dokumente, Videos, Musik, Archive. Das Prinzip ist immer dasselbe!
- Reale Anwendungsfälle: Die Fähigkeit, Ressourcen aus dem Netz herunterzuladen, ist die Grundlage für jede moderne Anwendung – von einfachen Wallpaper-Downloadern bis hin zu komplexen Systemen, die mit Social-Media-APIs arbeiten.
Wir betrachten zwei Ansätze: der schnelle „Postdienst“ — über URL, und die „professionelle Spedition“ — über HttpClient mit Kontrolle von Headern, Statuscodes und Timeouts.
2. Der einfachste Weg: die Klasse URL
In Java gibt es die Klasse URL, die eine Netzwerkadresse darstellt. Sie erlaubt es, eine Verbindung zu öffnen und einen Datenstrom (Typ InputStream) zu erhalten. Mit diesem Stream kann man genauso arbeiten wie mit einer Datei: Bytes lesen, in einen anderen Stream kopieren, als Text verarbeiten.
Im Grunde ist die Verwendung von URL der kürzeste Weg von einem Link im Browser zu einer Datei auf der Festplatte.
Erstes minimales Beispiel
Angenommen, wir haben einen Link zu einem Bild:
URL url = new URL("https://example.com/image.jpg");
Files.copy(url.openStream(), Path.of("a.jpg"));
Nur zwei Zeilen. Was passiert hier?
- Wir erzeugen ein Objekt URL und übergeben ihm die Zeichenkette mit der Bildadresse.
- Wir rufen die Methode openStream() auf, die eine Netzwerkverbindung öffnet und einen InputStream zurückgibt.
- Mit der Methode Files.copy kopieren wir den Inhalt des Streams in die Datei "a.jpg".
Ergebnis: Das Bild aus dem Internet wird in unserem Arbeitsordner gespeichert.
Variante mit transferTo
Es gibt noch eine weitere Möglichkeit, Daten aus einem Stream in eine Datei zu übertragen:
InputStream in = new URL("https://example.com/image.jpg").openStream();
in.transferTo(Files.newOutputStream(Path.of("b.jpg")));
Hier sagen wir ausdrücklich: Nimm den Stream in und „pump“ alle Daten mit der Methode transferTo in den Stream, der die Datei "b.jpg" schreibt.
Das Ergebnis ist dasselbe: Das Bild landet auf der Festplatte.
Wichtige Details
Dieser Ansatz ist sehr einfach, hat aber seine Feinheiten. Zum einen prüfen wir überhaupt nicht, was genau über den Link zurückkommt. Vielleicht ist es ein Bild. Es könnte aber auch eine HTML-Seite mit dem Fehler 404 sein. In beiden Fällen wird die Datei gespeichert. Der Unterschied zeigt sich erst später, wenn Sie versuchen, sie in einem Bildbetrachter zu öffnen.
3. Moderner Ansatz: HttpClient
Seit Java 11 gehört ein neues Werkzeug zur Standardbibliothek: HttpClient. Damit lassen sich HTTP-Anfragen auf modernem Niveau ausführen: mit den Methoden GET, POST und anderen arbeiten, Timeouts steuern, Header und Statuscodes überwachen, Redirects verarbeiten.
Für das Herunterladen von Bildern ist das besonders nützlich: Wir können sicherstellen, dass die Datei wirklich erfolgreich geladen wurde und nicht etwa ein Fehler zurückkam.
Minimales Beispiel mit HttpClient
URI uri = URI.create("https://example.com/image.jpg"); // URI - modernere Version von URL
HttpClient client = HttpClient.newHttpClient(); // HttpClient-Objekt erstellen
HttpRequest request = HttpRequest.newBuilder(uri).build(); // "Request"-Objekt erstellen
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("c.jpg"), response.body());
Schritt für Schritt:
- Wir erstellen den Client HttpClient.
- Wir konstruieren die Anfrage HttpRequest an die angegebene Adresse.
- Wir senden die Anfrage und erhalten die Antwort als Byte-Array: HttpResponse.BodyHandlers.ofByteArray().
- Wir schreiben diese Bytes mit Files.write in die Datei "c.jpg".
Das Ergebnis ist dasselbe Bild, aber jetzt haben wir mehr Informationen darüber, wie der Server geantwortet hat.
Statuscode prüfen
Das Objekt response verfügt über die Methode statusCode(). Damit können wir prüfen, ob der Server eine erfolgreiche Antwort geliefert hat:
if (response.statusCode() == 200)
{
Files.write(Path.of("ok.jpg"), response.body());
}
else
{
System.out.println("Fehler: Code " + response.statusCode());
}
Jetzt wissen wir genau, dass wir wirklich das Erwartete gespeichert haben – und nicht etwa eine Fehlerseite.
Header abrufen
Angenommen, wir wollen den Inhaltstyp prüfen:
String type = response.headers().firstValue("Content-Type").orElse("unbekannt");
System.out.println("Inhaltstyp: " + type);
Wenn der Server "image/png" oder "image/jpeg" zurückgibt, ist es tatsächlich ein Bild. Kommt hingegen "text/html", ist das ein Warnsignal.
Timeouts
Damit das Programm nicht hängen bleibt, wenn der Server lange braucht, kann man ein Limit setzen:
URI uri = URI.create("https://example.com/image.jpg");
HttpRequest req = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(5))
.build();
Wenn der Server nicht innerhalb von 5 Sekunden antwortet, wird die Anfrage mit einem Fehler beendet.
Redirects verarbeiten
Manchmal führen Links nicht direkt zur Datei, sondern zunächst auf eine Weiterleitungsseite. Automatisches Folgen von Redirects lässt sich so einschalten:
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
4. Praktische Szenarien
Mehrere Bilder hintereinander herunterladen
Oft müssen nicht nur ein, sondern dutzende oder hunderte Bilder geladen werden. Zum Beispiel schreiben Sie ein Programm, das Benutzer-Avatare oder Produktfotos speichert. Mit HttpClient erledigt man das in einer Schleife:
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());
}
}
Dateigröße prüfen
Es ist mitunter hilfreich zu wissen, wie viele Bytes das Bild hat. Der Server kann das im Header "Content-Length" mitteilen:
String length = response.headers().firstValue("Content-Length").orElse("?");
System.out.println("Größe: " + length + " Bytes");
Wenn es keinen Header gibt, kann man immer einfach response.body().length verwenden.
Bild im Programm laden und anzeigen
Manchmal muss das Bild nicht nur heruntergeladen, sondern gleich angezeigt werden. Dafür kann man die Bibliothek javax.imageio.ImageIO verwenden:
InputStream in = new URL("https://example.com/pic.png").openStream();
BufferedImage img = ImageIO.read(in);
System.out.println("Breite: " + img.getWidth() + ", Höhe: " + img.getHeight());
So können wir das Bild direkt im Speicher prüfen, ohne es überhaupt auf die Festplatte zu schreiben.
GO TO FULL VERSION