1. Introduction
Working with files and networking is one of the most important topics in programming. Almost any modern application interacts with the internet in one way or another: it downloads data, sends requests, receives images or documents. Even a simple program like a chat client or a music player inevitably deals with loading resources.
Today we’ll figure out how to do this in Java using the most visual and pleasant payload — images. Why images?
- Immediate feedback: If everything worked, you simply open the downloaded file and see the image. That’s an instant, clear result.
- A universal skill: Images are not just pictures, but binary data — “raw bytes”. Once you learn to work with them, you can download anything: documents, video, music, archives. The principle is the same!
- Real-world tasks: The ability to download resources from the network is foundational for building any modern application, from simple wallpaper downloaders to complex systems that work with social network APIs.
We’ll cover two approaches: a quick “postal service” — via URL, and a “professional transport company” — via HttpClient with control over headers, statuses, and timeouts.
2. The simplest way: the URL class
Java has a URL class that represents a network address. It lets you open a connection and get a data stream (type InputStream). You can work with this stream just like with a file: read bytes, copy to another stream, process it as text.
In essence, using URL is the shortest path from a link in the browser to a file on disk.
First minimal example
Suppose we have a link to an image:
URL url = new URL("https://example.com/image.jpg");
Files.copy(url.openStream(), Path.of("a.jpg"));
Just two lines. What’s going on here?
- We create a URL object, passing it the string with the image address.
- We call the openStream() method, which opens a network connection and returns an InputStream.
- Using Files.copy, we copy the stream contents to the file "a.jpg".
Result: the image from the internet is saved to our working folder.
Variant with transferTo
There’s another way to pipe data from a stream to a file:
InputStream in = new URL("https://example.com/image.jpg").openStream();
in.transferTo(Files.newOutputStream(Path.of("b.jpg")));
Here we explicitly say: take the in stream and “transfer” all the data with the transferTo method into the stream that writes the file "b.jpg".
The result will be the same: the image will end up on disk.
Important details
This approach is very simple, but it has caveats. First, we don’t check what actually came from the link. It may be an image. Or it may be an HTML page with a 404 error. In both cases the file will be saved. The difference will only become apparent later, when you try to open it in an image viewer.
3. Modern approach: HttpClient
Starting with Java 11, a new tool — HttpClient — was added to the standard library. It allows you to make HTTP requests at a modern level: work with GET, POST, and other methods, manage timeouts, track headers and statuses, and handle redirects.
For downloading images, this is especially useful: we can make sure the file was actually downloaded successfully rather than an error being returned.
Minimal example with HttpClient
URI uri = URI.create("https://example.com/image.jpg"); // URI is a more modern version of URL
HttpClient client = HttpClient.newHttpClient(); // Create an HttpClient object
HttpRequest request = HttpRequest.newBuilder(uri).build(); // Create a "request" object
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
Files.write(Path.of("c.jpg"), response.body());
Now step by step:
- Create an HttpClient.
- Construct an HttpRequest for the given address.
- Send the request and get the response as a byte array: HttpResponse.BodyHandlers.ofByteArray().
- Write these bytes to a file with Files.write — "c.jpg".
Result — the same image, but now we have more information about how exactly the server responded.
Checking the response status
The response object has a statusCode() method. With it, we can verify that the server returned a successful response:
if (response.statusCode() == 200)
{
Files.write(Path.of("ok.jpg"), response.body());
}
else
{
System.out.println("Error: code " + response.statusCode());
}
Now we know for sure that we saved exactly what we expected, not an error page.
Reading headers
Suppose we want to check the content type:
String type = response.headers().firstValue("Content-Type").orElse("unknown");
System.out.println("Content type: " + type);
If the server returned "image/png" or "image/jpeg", then it’s indeed an image. If "text/html" came back, that’s a red flag.
Timeouts
To prevent the program from hanging if the server responds slowly, you can set a limit:
URI uri = URI.create("https://example.com/image.jpg");
HttpRequest req = HttpRequest.newBuilder(uri)
.timeout(Duration.ofSeconds(5))
.build();
If the server doesn’t respond within 5 seconds, the request will fail with an error.
Handling redirects
Sometimes links don’t lead directly to a file but first to a redirect page. You can enable automatic following of redirects like this:
HttpClient client = HttpClient.newBuilder()
.followRedirects(HttpClient.Redirect.NORMAL)
.build();
4. Practical scenarios
Downloading multiple images in a row
Often you need to download not one but dozens or hundreds of images. For example, you’re writing a program that saves user avatars or product photos. With HttpClient, you can do this in a loop:
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());
}
}
Checking the file size
It can be useful to know how many bytes the image has. The server may report this in the "Content-Length" header:
String length = response.headers().firstValue("Content-Length").orElse("?");
System.out.println("Size: " + length + " bytes");
If the header is missing, you can always just take response.body().length.
Downloading and displaying an image in the program
Sometimes you need not only to download an image but also to display it right away. To do this, you can use the javax.imageio.ImageIO library:
InputStream in = new URL("https://example.com/pic.png").openStream();
BufferedImage img = ImageIO.read(in);
System.out.println("Width: " + img.getWidth() + ", height: " + img.getHeight());
This way, we can inspect the image directly in memory, without even saving it to disk.
GO TO FULL VERSION