Одним із найкорисніших нововведень, що були включені до випуску 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.file
API, але виконує все в пам’яті як віртуальна файлова система.
Використовуючи бібліотеку, ви можете визначати власні шляхи до каталогів і файлів програмним шляхом. Отже, ми створюємо власну структуру віртуальних каталогів і передаємо її 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, особливо коли використовується разом із HttpServer
API нижчого рівня.
Підписуйтеся на ProIT у Telegram, щоб не пропустити жодну публікацію!