JavaRush /Курсы /JAVA 25 SELF /Скачиваем картинки из интернета

Скачиваем картинки из интернета

JAVA 25 SELF
12 уровень , 4 лекция
Открыта

1. Введение

Работа с файлами и сетью — одна из важнейших тем в программировании. Почти любое современное приложение так или иначе взаимодействует с интернетом: загружает данные, отправляет запросы, получает изображения или документы. Даже простая программа вроде чат-клиента или музыкального проигрывателя обязательно имеет дело с загрузкой ресурсов.

Сегодня мы разберёмся, как это делать в Java, на примере самого наглядного и приятного груза — картинок. Почему именно они?

  • Видно сразу: Если всё сработало, вы просто открываете скачанный файл и видите изображение. Это моментальный и понятный результат.
  • Универсальный навык: Картинки — это не просто картинки, а бинарные данные, “сырые байты”. Научившись работать с ними, вы сможете скачивать что угодно: документы, видео, музыку, архивы. Принцип один и тот же!
  • Реальные задачи: Умение скачивать ресурсы из сети — это фундамент для создания любого современного приложения, от простых загрузчиков обоев до сложных систем, работающих с API социальных сетей.

Мы разберёмся двумя подходами: быстрый “почтовый сервис” — через 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"));

Всего две строчки. Что здесь происходит?

  1. Создаём объект URL, передавая ему строку с адресом картинки.
  2. Вызываем метод openStream(), который открывает сетевое соединение и возвращает поток InputStream.
  3. С помощью метода 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".

Результат будет тот же: картинка окажется на диске.

Важные детали

Такой подход очень прост, но у него есть нюансы. Во-первых, мы никак не проверяем, что именно пришло по ссылке. Может быть, это картинка. А может быть — HTML-страница с ошибкой 404. В обоих случаях файл сохранится. Разница будет заметна только потом, когда вы попробуете открыть его в просмотрщике изображений.

3. Современный способ: HttpClient

Начиная с Java 11 в стандартную библиотеку вошёл новый инструмент — HttpClient. Он позволяет делать HTTP-запросы на современном уровне: работать с методами 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());

Теперь по шагам:

  1. Создаём клиента HttpClient.
  2. Конструируем запрос HttpRequest на указанный адрес.
  3. Отправляем запрос и получаем ответ как массив байтов: HttpResponse.BodyHandlers.ofByteArray().
  4. Записываем эти байты в файл методом Files.write"c.jpg".

Результат — та же картинка, но теперь у нас есть больше информации о том, как именно сервер ответил.

Проверка статуса ответа

У объекта response есть метод statusCode(). С его помощью можно убедиться, что сервер вернул успешный ответ:

if (response.statusCode() == 200)
{
    Files.write(Path.of("ок.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. Практические сценарии

Загрузка нескольких картинок подряд

Часто требуется скачать не одну, а десятки или сотни картинок. Например, вы пишете программу, которая сохраняет аватарки пользователей или фотографии товаров. С помощью 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());

Таким образом, мы можем проверить картинку сразу в памяти, даже не сохраняя её на диск.

1
Задача
JAVA 25 SELF, 12 уровень, 4 лекция
Недоступна
Скачивание файла через URL
Скачивание файла через URL
1
Задача
JAVA 25 SELF, 12 уровень, 4 лекция
Недоступна
Скачивание файла с помощью transferTo
Скачивание файла с помощью transferTo
1
Задача
JAVA 25 SELF, 12 уровень, 4 лекция
Недоступна
Скачивание файла через HttpClient
Скачивание файла через HttpClient
1
Задача
JAVA 25 SELF, 12 уровень, 4 лекция
Недоступна
Получение типа содержимого и размера файла
Получение типа содержимого и размера файла
Комментарии (14)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Cherepoq Уровень 18
27 февраля 2026
Мдааа, бонусный уровень а по ощущениям 5 отдельных уровней сжатые в 1 лекцию
Sergey Lunev Уровень 41
29 ноября 2025
Классный урок, единственно возможно в статье стоит упомянуть, что конструктор вида: new URL("https://example.com/image.jpg") с 20-й версии Java считается устаревшим (depricated). Речь все таки про 25-ю Java
Yaroslav Уровень 57
27 ноября 2025
// Готовим GET-запрос к нужному URL Как? Да, этого не было в лекции... читаем здесь
15 декабря 2025
спасибо
Виктор Уровень 13
14 ноября 2025
Кайф, наконец-то что-то стоящее! а то все вода уровня старших классов. кто не осилил загуглите "http сервер клиент запрос ответ"
Kidze Уровень 26
14 ноября 2025
Сложно воспринимается, как будто надо было разбить на несколько лекций тему, с отдельным разбором каждого из классов и их методов, вместо вываливания сразу всего.
Ksanders Уровень 32
17 октября 2025
Ну, как говорятся, очень интереcно - но не очень понятно. Сама по себе лекция действительно интересная - прямо переход от теории к практике.

HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
Но когда видишь такую конструкцию, то что происходит внутри метода send - не очень понятно. Опять же не сказано про обработку исключения которое может при этом возникнуть (IOException | InterruptedException). Как будто в лекции просто дан набор методов которые мне, как новичку, запомнить вряд ли удастся + много непонятных конструкций, которые просто есть, но не объcняется что это и зачем используется. Как будто проще почитать лекцию как ознакомительную, не вникая (учитывая что она бонусная, возможно в этом и смысл).
Александр Уровень 50
16 октября 2025
Коллеги, нужно решить прикладную задачу. Коротко: Айфон дает имена тупо - Image001 и так далее. Андроид - умно, используя дату съемки. Хочу переименовать все айфоновские фотки в стиле андроида. Поподробнее: Есть папка с фото (Image01.jpg, Image02.jpg и т.д.). Нужно поменять имена файлов, указав в качестве названия значения из атрибута "дата изменения". Все атрибуты исходных файлов должны быть сохранены. Заглянул сюда: javax.imageio.metadata.IIOMetadata, но понятного мало. public abstract Node getAsTree(String formatName) // похоже, что это нужный метод, но возвращает какую-то Ноду, да и еще в XML-формате. Выше моего разумения. Может, кто подскажет, куда копать?
Grrbrr7 Уровень 29
12 ноября 2025
Бро, дипсик предлагает вот такими инструментами пользоваться: import org.apache.commons.imaging.Imaging; import org.apache.commons.imaging.common.ImageMetadata; import org.apache.commons.imaging.formats.jpeg.JpegImageMetadata; import org.apache.commons.imaging.formats.tiff.TiffField; import org.apache.commons.imaging.formats.tiff.constants.ExifTagConstants; import java.io.ByteArrayInputStream; import java.util.Date; или import com.drew.imaging.ImageMetadataReader; import com.drew.metadata.Metadata; import com.drew.metadata.exif.ExifSubIFDDirectory; import java.io.ByteArrayInputStream; import java.util.Date; Короче, юзай ИИ
Aidar Уровень 44
16 октября 2025
Почему компилятор просит в таске (Скачивание файла с помощью transferTo ) условию которую он не включает : «Используй try-with-resources для автоматического закрытия потоков.» хотя даже в вариации решения самого компилятора (JavaRush) оно отсутствует ?
Nikita Уровень 42
12 сентября 2025
Интересно где у нас хоть что то про метод GET, я с нуля и этой информации негде брать, чтобы не сломав голову я мог догадаться, что именно метод! такой есть. Хотя бы пояснение что надо поискать информацию самому, а то 3 задачи по лекции а 4 по "инерции" нет.
16 сентября 2025
Никита, как вариант: про API запросы
German Malykh Уровень 31
22 сентября 2025
Если провалиться в HttpRequest, то в Javadoc описании есть информация о том, что GET указан по умолчанию (метод запроса устанавливается по умолчанию в GET). Решить задачи можно и через явное указание (так будет прозрачнее для понимания)

HttpRequest request = HttpRequest.newBuilder(url).GET().build();
Описание из реализации HttpRequest

    /**
     * A builder of {@linkplain HttpRequest HTTP requests}.
     *
     * <p> Instances of {@code HttpRequest.Builder} are created by calling
     * {@link HttpRequest#newBuilder()}, {@link HttpRequest#newBuilder(URI)},
     * or {@link HttpRequest#newBuilder(HttpRequest, BiPredicate)}.
     *
     * <p> The builder can be used to configure per-request state, such as: the
     * request URI, the request method (default is GET unless explicitly set),
     * specific request headers, etc. Each of the setter methods modifies the
     * state of the builder and returns the same instance. The methods are not
     * synchronized and should not be called from multiple threads without
     * external synchronization. The {@link #build() build} method returns a new
     * {@code HttpRequest} each time it is invoked. Once built an {@code
     * HttpRequest} is immutable, and can be sent multiple times.
     *
     * <p> Note, that not all request headers may be set by user code. Some are
     * restricted for security reasons and others such as the headers relating
     * to authentication, redirection and cookie management may be managed by
     * specific APIs rather than through directly user set headers.
     *
     * @since 11
     */
    public interface Builder {