ProIT: медіа для профі в IT
5 хв

Лайфхаки: Java 18 Simple Web Server дозволяє використовувати інструмент командного рядка або API для розміщення файлів. Ось як це працює

author avatar ProIT NEWS

Одним із найкорисніших нововведень, що були включені до випуску Java 18, є новий Simple Web Server, який полегшує створення та налаштування HTTP файлового сервера. Також він надає API, яке розширює наявний пакет httpserver для побудови простих випадків використання.

Новий Simple Web Server – це корисний інструмент, який кожен розробник Java повинен мати у своєму арсеналі. Деталі розбирали в InfoWorld.

Простий вебсервер у командному рядку

Нова команда Java jwebserverспрощує запуск основного вебсервера. Це аналог популярного інструменту SimpleHTTPServer у світі Python.

Перший крок – переконатися, що ви використовуєте Java 18 або пізніший реліз. Введіть команду java --version, щоб дізнатися, яким релізом ви зараз користуєтеся. Рекомендовано використовувати SDKMan для керування встановленням JDK. Це особливо корисно для роботи з кількома версіями.

Основи jwebserver

Основне, що ви можете зробити з Java Simple Web Server, – це обслуговувати поточний каталог порту 8000. Просто введіть команду, показану в лістингу 1.

Лістинг 1. No-arg web server


$ jwebserver

Binding to loopback by default. For all interfaces use "-b 0.0.0.0" or "-b ::".
Serving /home/matthewcarltyson and subdirectories on 127.0.0.1 port 8000
URL http://127.0.0.1:8000/

Звідти, якщо ви зайдете у свій браузер і відвідаєте localhost:8000, то побачите список файлової системи, як показано на малюнку 1.

Налаштування командного рядка

Існує кілька поширених речей, які вам можуть знадобитися для точного налаштування простого вебсервера у командному рядку. Наприклад, ви можете змінити порт, адресу для прив’язки (мережевий інтерфейс для прослуховування) і каталог для обслуговування.

У лістингу 2 можна побачити, як слухати порт 8080 на всіх інтерфейсах і в /foo/barкаталозі.

Лістинг 2. Прослуховування порту 8080, усі інтерфейси, /foo/bar


$ jwebserver -b 0.0.0.0 -p 8081 -d /foo/bar

You can get a list of all the options with $ jwebserver -h.

Як бачите, jwebserverінструмент командного рядка дає змогу обслуговувати статичні файли за допомогою найпростішого синтаксису. Далі ми розглянемо API простого вебсервера.

Використання API простого вебсервера

Простий вебсервер Javadoc є хорошою відправною точкою для вивчення API. Клас SimpleFileServerіснує в com.sun.net.httpserverпакеті. (Цей пакет також містить API більш низького рівня для створення вебсерверів. Пакет httpserverрозширює цю функціональність для спрощення вимог.) jwebserverІнструмент CLI використовує SimpleFileServerдля виконання своєї роботи, і ми також можемо використовувати його програмно.

Клас SimpleFileServerобробляє лише GET та HTTP/1.1. Однак з ним можна робити деякі цікаві речі. Наприклад, цей вступ до роботи з простим вебсервером пропонує спосіб використання проєкту файлової системи Google Java у пам’яті для підробки файлової системи для handler.

Ми збираємося використати ідею внутрішньої файлової системи для модифікації FileHandler у SimpleFileServerфактичного обслуговування віртуальної файлової системи з пам’яті. Потім використаємо httpserverпакет для обробки POST, щоб додати faux file до faux file system.

Обслуговування віртуальної файлової системи з пам’яті

Для початку давайте створимо швидкий проєкт Maven за допомогою такої команди:


$ mvn archetype:generate -DgroupId=.com.infoworld -DartifactId=jsws -DarchetypeArtifactId=maven-archetype-quickstart

Тепер перейдіть у новий каталог /jsws.

Встановіть у pom.xml версії компілятора та джерела 18, як описано тут.

Далі додайте Google jimfsдо залежностей, як показано в лістингу 3.

Лістинг 3. Залежність файлової системи google Java у пам’яті


<dependency>
  <groupId>com.google.jimfs</groupId>
  <artifactId>jimfs</artifactId>
  <version>1.3.0</version>
</dependency>

Тепер ми можемо змінити файл  src/main/java/App.javaфайл, щоб обслуговувати fake file system. Ви можете побачити код для цього в лістингу 4.

Лістинг 4. Обслуговування файлової системи в пам’яті за допомогою SimpleFileServer


package com.infoworld;

import java.util.List;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.sun.net.httpserver.SimpleFileServer;

import static com.sun.net.httpserver.SimpleFileServer.OutputLevel;

public class Mem {
  private static final InetSocketAddress ADDR =
    new InetSocketAddress("0.0.0.0", 8080);

  public static void main( String[] args ) throws Exception {
    Path root = createDirectoryHierarchy();
    var server = SimpleFileServer.createFileServer(ADDR, root, OutputLevel.VERBOSE);
    server.start();
  }

  private static Path createDirectoryHierarchy() throws IOException {
    FileSystem fs = Jimfs.newFileSystem(Configuration.unix());
    Path root = fs.getPath("/");

    Files.createDirectories(root.resolve("test/test2"));

    Path foo = fs.getPath("/foo");
    Files.createDirectory(foo);

    Path hello = foo.resolve("hello.txt"); 
    Files.write(hello,  List.of("hello", "world"), StandardCharsets.UTF_8);

    return root;
  }
}

Ідея в лістингу 4 полягає у моделюванні стандартного API локальної файлової системи за допомогою бібліотеки jimfs з відкритим вихідним кодом Google, яка реалізує java.nio.fileAPI, але виконує все в пам’яті як віртуальна файлова система.

Використовуючи бібліотеку, ви можете визначати власні шляхи до каталогів і файлів програмним шляхом. Отже, ми створюємо власну структуру віртуальних каталогів і передаємо її SimpleFileServerяк обробнику файлів.

Ми налаштовуємо SimpleFileServerклас програмно:


var server = SimpleFileServer.createFileServer(ADDR, root, OutputLevel.VERBOSE);

Це приймає інтернет-адресу для прив’язки, як ми бачили у командному рядку. У цьому випадку ми передаємо невизначений інтерфейс і порт 8080. Після цього йде коренева файлова система. Для цього прикладу ми передаємо об’єкт Path, створений нашим createDirectoryHierarchy()методом.

Метод createDirectoryHierarchy()використовує jimfsдля створення обʼєкта Path: FileSystem fs = Jimfs.newFileSystem(Configuration.unix());. Потім він заповнює Pathфайлами та каталогами.

API jimfsдля створення шляхів і файлів із вмістом неважко зрозуміти. Наприклад, ми створюємо один за допомогою Path hello = foo.resolve("hello.txt");. Ви можете використовувати більшість об’єктів так, ніби вони були звичайними шляхами Java NIO.

Тепер, якщо ми запустимо цей код і відвідаємо localhost:8080, то побачимо список каталогів.

Створення віртуального файлу

Давайте розвинемо цю ідею ще на один крок і додамо можливість завантажувати новий файл. Ми можемо використовувати пакет  com.sun.net.httpserver, щоб прийняти запит POST, який завантажить новий файл у нашу файлову систему в пам’яті. Ви можете побачити це в лістингу 5.

Лістинг 5. Завантаження нового файлу до файлової системи в пам’яті


package com.infoworld;

import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;

import java.io.IOException;
import java.io.OutputStream;

import java.net.InetSocketAddress;
import java.util.List;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import com.google.common.jimfs.Configuration;
import com.google.common.jimfs.Jimfs;
import com.sun.net.httpserver.SimpleFileServer;

import static com.sun.net.httpserver.SimpleFileServer.OutputLevel;

public class App {
  public static void main( String[] args ) throws Exception {
    // same config...
    server.start();

    // Create the HTTP server with the UploadHandler using the same 'root' path
    HttpServer httpServer = HttpServer.create(new InetSocketAddress("0.0.0.0", 8081), 0);
    httpServer.createContext("/upload", new UploadHandler(root));
    httpServer.start();
  }

  private static Path createDirectoryHierarchy() throws IOException {
    // same ...
  }

  // Handler to process POST requests and upload files
  static class UploadHandler implements HttpHandler {
    private final Path root;

    public UploadHandler(Path root) {
      this.root = root;
    }

    @Override
       public void handle(HttpExchange exchange) throws IOException {
       if ("POST".equalsIgnoreCase(exchange.getRequestMethod())) {
         String filename = exchange.getRequestHeaders().getFirst("filename");

         if (filename != null) {
           Path newFilePath = root.resolve(filename);
          try (OutputStream os = Files.newOutputStream(newFilePath)) {
            exchange.getRequestBody().transferTo(os);
          }
          String response = "File uploaded successfully: " + filename;
          exchange.sendResponseHeaders(200, response.getBytes(StandardCharsets.UTF_8).length);
          try (OutputStream os = exchange.getResponseBody()) {              
            os.write(response.getBytes(StandardCharsets.UTF_8));
          }
        } else {
          exchange.sendResponseHeaders(400, -1); // Bad Request
        }
      } else {
        exchange.sendResponseHeaders(405, -1); // Method Not Allowed
      }
    }
  }
}

У лістингу 5 ми зберігаємо більшу частину класу незмінною, але створюємо екземпляр  HttpServer, який прослуховує порт 8081. Його налаштовано за допомогою нашого користувацького uploadHandler, який приймає завантажені дані та використовує їх для запису нового файлу до кореневого шляху, який ми створили у createDirectoryHierarchy.

Щоб перевірити це, можна запустити весь кластер серверів за допомогою:


$ mvn clean install exec:java -Dexec.mainClass="com.infoworld.Mem"

Ви можете надіслати новий файл на сервер за допомогою запиту CURL, як показано в лістингу 6.

Лістинг 6. CURL POST файл


$ touch file.txt
$ curl -X POST -H "filename: file.txt" -d "@file.txt" http://localhost:8081/upload

File uploaded successfully: file.txt

Коли ви перезавантажите списки файлівlocalhost:8080/, то побачите новий file.txtі зможете клацнути його та переглянути його вміст.

Висновок

Simple Web Server є бажаним доповненням до набору інструментів Java. Він не лише дуже спрощує розміщення файлів за допомогою CLI, але й відкриває деякі цікаві можливості за допомогою API, особливо коли використовується разом із HttpServerAPI нижчого рівня.

Підписуйтеся на ProIT у Telegram, щоб не пропустити жодну публікацію!‌‌‌‌

Приєднатися до company logo
Продовжуючи, ти погоджуєшся з умовами Публічної оферти та Політикою конфіденційності.