1. Introduzione
Lavorare con file e rete è uno dei temi più importanti nella programmazione. Quasi ogni applicazione moderna in un modo o nell’altro interagisce con Internet: scarica dati, invia richieste, riceve immagini o documenti. Anche un semplice programma come un client di chat o un lettore musicale ha necessariamente a che fare con il download di risorse.
Oggi vedremo come farlo in Java prendendo come esempio il carico più evidente e piacevole — le immagini. Perché proprio loro?
- Risultato immediato: Se tutto ha funzionato, apri semplicemente il file scaricato e vedi l’immagine. È un risultato istantaneo e chiaro.
- Competenza universale: Le immagini non sono soltanto immagini, ma dati binari, «byte grezzi». Imparando a lavorarci, potrai scaricare di tutto: documenti, video, musica, archivi. Il principio è lo stesso!
- Esigenze reali: Saper scaricare risorse dalla rete è fondamentale per creare qualsiasi applicazione moderna, dai semplici downloader di sfondi ai sistemi complessi che lavorano con le API dei social network.
Useremo due approcci: il «servizio postale» rapido — tramite URL, e la «società di trasporto professionale» — tramite HttpClient con controllo di header, status e timeout.
2. Il modo più semplice: la classe URL
In Java esiste la classe URL, che rappresenta un indirizzo di rete. Consente di aprire una connessione e ottenere un flusso di dati (tipo InputStream). Con questo flusso si può lavorare come con un file: leggere byte, copiarli in un altro flusso, trattarli come testo.
In sostanza, usare URL è la via più corta dal link nel browser al file su disco.
Primo esempio minimale
Supponiamo di avere un link a un’immagine:
URL url = new URL("https://example.com/image.jpg");
Files.copy(url.openStream(), Path.of("a.jpg"));
Solo due righe. Che cosa succede qui?
- Creiamo un oggetto URL, passandogli la stringa con l’indirizzo dell’immagine.
- Chiamiamo il metodo openStream(), che apre la connessione di rete e restituisce un flusso InputStream.
- Con il metodo Files.copy copiamo il contenuto del flusso nel file "a.jpg".
Risultato: l’immagine da Internet viene salvata nella nostra cartella di lavoro.
Variante con transferTo
C’è un altro modo per trasferire i dati da un flusso a un file:
InputStream in = new URL("https://example.com/image.jpg").openStream();
in.transferTo(Files.newOutputStream(Path.of("b.jpg")));
Qui diciamo esplicitamente: prendi il flusso in e «trasferisci» tutti i dati con il metodo transferTo in un flusso che scrive il file "b.jpg".
Il risultato sarà lo stesso: l’immagine finirà sul disco.
Dettagli importanti
Questo approccio è molto semplice, ma ha delle sfumature. Per prima cosa, non verifichiamo in alcun modo che cosa sia arrivato dal link. Potrebbe essere un’immagine. Oppure — una pagina HTML con l’errore 404. In entrambi i casi il file verrà salvato. La differenza si noterà solo dopo, quando proverai ad aprirlo nel visualizzatore di immagini.
3. Metodo moderno: HttpClient
A partire da Java 11 nella libreria standard è arrivato un nuovo strumento — HttpClient. Permette di effettuare richieste HTTP in modo moderno: usare i metodi GET, POST e altri, gestire i timeout, monitorare header e status, trattare i redirect.
Per scaricare immagini è particolarmente utile: possiamo assicurarci che il file sia stato effettivamente scaricato con successo e non sia stato ricevuto un errore.
Esempio minimale con HttpClient
URI uri = URI.create("https://example.com/image.jpg"); // URI - una versione più moderna di URL
HttpClient client = HttpClient.newHttpClient(); // Creiamo un oggetto HttpClient
HttpRequest request = HttpRequest.newBuilder(uri).build(); // Creiamo l'oggetto "richiesta"
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("c.jpg"), response.body());
Ora, passo per passo:
- Creiamo il client HttpClient.
- Costruiamo la richiesta HttpRequest per l’indirizzo indicato.
- Inviamo la richiesta e otteniamo la risposta come array di byte: HttpResponse.BodyHandlers.ofByteArray().
- Scriviamo questi byte su file con Files.write — "c.jpg".
Risultato — la stessa immagine, ma ora abbiamo più informazioni su come ha risposto il server.
Verifica dello status della risposta
L’oggetto response ha il metodo statusCode(). Con esso possiamo assicurarci che il server abbia restituito una risposta con esito positivo:
if (response.statusCode() == 200)
{
Files.write(Path.of("ok.jpg"), response.body());
}
else
{
System.out.println("Errore: codice " + response.statusCode());
}
Ora sappiamo con certezza di aver salvato esattamente ciò che ci aspettavamo, e non una pagina di errore.
Recupero degli header
Supponiamo di voler verificare il tipo di contenuto:
String type = response.headers().firstValue("Content-Type").orElse("sconosciuto");
System.out.println("Tipo di contenuto: " + type);
Se il server ha restituito "image/png" o "image/jpeg", significa che è davvero un’immagine. Se invece è arrivato "text/html", è un segnale d’allarme.
Timeout
Per evitare che il programma si blocchi se il server risponde lentamente, si può impostare un limite:
URI uri = URI.create("https://example.com/image.jpg");
HttpRequest req = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(5))
.build();
Se il server non risponde entro 5 secondi, la richiesta terminerà con un errore.
Gestione dei redirect
A volte i link non portano direttamente al file, ma prima a una pagina di reindirizzamento. Si può attivare il follow automatico dei redirect così:
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
4. Scenari pratici
Scaricare più immagini di seguito
Spesso occorre scaricare non una, ma decine o centinaia di immagini. Ad esempio, stai scrivendo un programma che salva gli avatar degli utenti o le foto dei prodotti. Con HttpClient lo si fa con un ciclo:
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());
}
}
Verifica della dimensione del file
Può essere utile sapere quanti byte ha l’immagine. Il server può comunicarlo nell’header "Content-Length":
String length = response.headers().firstValue("Content-Length").orElse("?");
System.out.println("Dimensione: " + length + " byte");
Se l’header non c’è, si può sempre prendere response.body().length.
Scaricare e visualizzare l’immagine nel programma
A volte un’immagine va non solo scaricata, ma anche mostrata subito. Per questo si può usare la libreria javax.imageio.ImageIO:
InputStream in = new URL("https://example.com/pic.png").openStream();
BufferedImage img = ImageIO.read(in);
System.out.println("Larghezza: " + img.getWidth() + ", altezza: " + img.getHeight());
In questo modo possiamo ispezionare l’immagine direttamente in memoria, senza neppure salvarla su disco.
GO TO FULL VERSION