1. Класс URL

Работу с потоками ввода-вывода мы уже изучили, работу с файлами изучили, что же изучить дальше? А как насчет работы с сетью и интернетом? Звучит многообещающе, не так ли?

В Java работать с интернетом не сложнее, чем работать с файлами. Ну разве что совсем чуть-чуть.

Для работы с ресурсами в интернете в Java есть специальный класс — URL. Он простой, как табуретка, в чем вы сейчас и убедитесь.

Получение странички из интернета

Как вы думаете, сколько строк кода нужно написать, чтобы скачать какой-нибудь текстовый файл из интернета и отобразить его содержимое на экране? 10? 100? 1000? А может быть, 5?

Код Примечание
URL url = new URL("https://javarush.ru");
InputStream input = url.openStream();
byte[] buffer = input.readAllBytes();
String str = new String(buffer);
System.out.println(str);
Создает объект URL с путем к странице
Получает InputStream у интернет-объекта
Читает все байты и возвращает массив байт
Преобразуем массив в строку
Выводим строку на экран

На экране отобразится содержимое HTML-файла:

Вывод на экран
<!DOCTYPE html><html lang="ru" class="light"><head>
    <meta charset="utf-8″>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1″>
    ...

Сравнение работы File и URL

URL — это такой аналог File или Path, только Path хранит путь к ресурсу в файловой системе, а URL — путь к ресурсу в интернете.

Вся магия происходит, когда мы за один вызов метода openStream() получаем сразу объект типа InputStream. Это же стандартный объект, и мы его уже изучили вдоль и поперек. Все становится очевидно уже после получения объекта типа InputStream. Ведь как вычитать из него данные, мы уже знаем.

Сравните: отличаются только две первые строчки, и то немного. Вот оно, преимущество стандартизации и работы с цепочками потоков данных:

Работа с интернетом Работа с файлом
URL url = new URL("https://javarush.ru");
InputStream input = url.openStream();

byte[] buffer = input.readAllBytes();
String str = new String(buffer);
System.out.println(str);
File file = new File("c:\\readme.txt");
InputStream input = new FileInputStream(file);

byte[] buffer = input.readAllBytes();
String str = new String(buffer);
System.out.println(str);


2. Класс URLConnection

Кроме простого чтения данных из интернета, мы еще можем и загружать туда данные. Загрузка данных — это куда более сложный процесс, чем считывание. Вам понадобится на несколько методов больше. Пример:

Код Примечание
URL url = new URL("https://javarush.ru");
URLConnection connection = url.openConnection();

// получили поток для отправки данных
OutputStream output = connection.getOutputStream();
output.write(1); // отправляем данные

// получили поток для чтения данных
InputStream input = connection.getInputStream();
int data = input.read(); // читаем данные
Создаем объект URL с путем к странице
Создаем двустороннее соединение


Получаем поток вывода
Выводим в него данные


Получаем поток ввода
Читаем из него данные

Обратите внимание, что тут мы больше не вызываем метод url.openStream(). Вместо этого мы идем по более длинному пути:

  • Сначала мы устанавливаем стабильное двустороннее соединение с помощью метода URLConnection openConnection()
  • Затем получаем поток для отправки данных с помощью метода connection.getOutputStream() и отправляем данные серверу
  • Затем получаем поток для чтения данных с помощью метода connection.getInputStream() и начинаем читать из него данные.

Контроль ресурсов

Строго говоря, мы должны все потоки обернуть в try-with-resources для безопасной работы. А еще не помешало бы обернуть голые InputStream и OutputStream во что-нибудь более удобное. Например, в PrintStream и BufferedReader.

Если мы все это сделаем, наш код будет выглядеть как-то так:

URL url = new URL("https://javarush.ru");
URLConnection connection = url.openConnection();

// отправляем данные
try (OutputStream output = connection.getOutputStream();
   PrintStream sender = new PrintStream(output))
{
   sender.println("Привет");
}

// читаем данные
try(InputStream input = connection.getInputStream();
   BufferedReader reader = new BufferedReader(new InputStreamReader(input)))
{
   while (reader.ready())
      System.out.println(reader.readLine());
}

3. Примеры работы с сетью

А давай-ка мы что-нибудь загрузим из интернета. И не просто загрузим, а сохраним на диск.

Например, давайте напишем программу, которая сохраняет на диск картинку с главной страницы Google.

В принципе ничего сложного. В самой простой интерпретации этот код будет выглядеть так:

Сохранение файла на диск
String image = "https://www.google.com/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png";
URL url = new URL(image);
InputStream input = url.openStream();

Path path = Path.of("c:\\GoogleLogo.png");
Files.copy(input, path);

С помощью трех первых срок мы получаем поток данных от интернет-ресурса — от картинки.

В четвертой строке мы создаем имя файла, в который будем сохранять картинку. Имя может быть любым, однако расширение файла должно совпадать с расширением картинки в интернете. Так локальные программы просмотра картинок будут правильно ее открывать.

Ну и наконец, последняя строка — это один из методов copy класса Files. У класса Files их несколько. Этот метод, который мы использовали, принимает в качестве первого параметра источник данных — байтовый поток (InputStream), а в качестве второго параметра — имя файла, куда нужно записывать данные.

Теоретически, если бы URL картинки был коротким, этот код вообще можно было бы записать в одну строку:

Копирование данных из потока в файл
Files.copy(
   new URL("https://www.google.com/logo.png").openStream(),
   Path.of("c:\\GoogleLogo.png")
);

Писать так, конечно же, не нужно, однако этот пример демонстрирует, насколько удобные и мощные в Java потоки ввода-вывода.