1. Клас URL

Роботу з потоками вводу й виводу ми вже вивчили, роботу з файлами вивчили. То що ж вивчати далі? А як щодо роботи з мережею та інтернетом? Звучить заманливо, чи не так?

У Java працювати з інтернетом не складніше, ніж працювати з файлами. Ну хіба що трошечки.

Для роботи з ресурсами в інтернеті в Java є спеціальний клас — URL. Він не складніший за табуретку, і ви зараз у цьому переконаєтеся.

Отримання сторінки з інтернету

На вашу думку, скільки рядків коду потрібно написати, щоб завантажити який-небудь текстовий файл з інтернету й відобразити його вміст на екрані? 10? 100? 1000? А може, 5?

Код Примітка
URL url = new URL("https://javarush.com");
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.com");
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.com");
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.com");
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.