Java 18 版本(2022 年 3 月)中包含的最方便的新功能之一是新的 Simple Web Server,它可以轻松启动和配置 HTTP 文件服务器。它还公开了一个 API,该 API 扩展了现有httpserver包以构建简单的用例。
新的 Simple Web Server 是一个有用的工具,每个 Java 开发人员都应该拥有它。让我们来看看!
目录
- 命令行上的 Java 简单 Web 服务器
- 使用 Java 的简单 Web 服务器 API
- 从内存中提供虚拟文件系统
- 快速、简单且灵活的 Web 服务器
命令行上的 Java 简单 Web 服务器
Java 的新jwebserver命令使运行基本 Web 服务器变得简单。它类似于Python世界中流行的SimpleHTTPServer工具。
第一步是确保您运行的是 Java 18 或更高版本。键入java --version以找出您当前正在运行的版本。我建议使用SDKMan来管理 JDK 安装。它对于处理多个版本特别有用。
jweb服务器基础知识
使用 Java Simple Web Server 可以做的最基本的事情是在端口 8000 上为当前目录提供服务。只需输入清单 1 中所示的命令即可。
清单 1. 无参数 Web 服务器
$ 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 所示。
图 1. 在端口 8000 上运行的简单 Web 服务器实例。
配置命令行
您可能需要执行几项常见操作来在命令行上微调 Simple Web Server。例如,您可以更改端口、要绑定的地址(要侦听的网络接口)以及要提供服务的目录。
在清单 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命令行工具可以使用最简单的语法来提供静态文件。接下来,我们将了解简单 Web 服务器 API。
使用 Java 的简单 Web 服务器 API
Simple Web Server Javadoc是学习 API 的一个很好的起点。该类SimpleFileServer存在于com.sun.net.httpserver包中。(这个包还包含用于构建 Web 服务器的较旧的、较低级别的 API。该httpserver包扩展了该功能以满足更简单的需求。)jwebserverCLI 工具用于SimpleFileServer完成其工作,我们也可以以编程方式使用它。
该类SimpleFileServer仅处理GETHTTP/1.1。不过,我们可以用它做一些有趣的事情。例如,本 关于使用 Simple Web Server 的介绍建议了一种使用 Google Java 内存中文件系统项目为处理程序伪造文件系统的方法。
我们将使用内存中文件系统的思想来修改 inFileHandler以SimpleFileServer实际为内存中的虚拟文件系统提供服务。然后,我们将使用该httpserver包来处理POST将人造文件添加到人造文件系统中。
从内存中提供虚拟文件系统
首先,让我们使用以下命令创建一个快速 Maven 项目:
$ mvn archetype:generate -DgroupId=.com.infoworld -DartifactId=jsws -DarchetypeArtifactId=maven-archetype-quickstart
现在,CD 进入新/jsws目录。
18在 pom.xml 中设置编译器和源版本,如此处所述。
接下来,将 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文件以服务于假文件系统。您可以在清单 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 中的想法是使用Google 的开源 jimfs 库来模拟标准本地文件系统 API ,该库实现了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然后它用文件和目录填充。用于创建路径和包含内容的文件的APIjimfs并不难掌握;例如,我们创建一个带有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,您可以单击它并查看其内容。
快速、简单且灵活的 Web 服务器
Simple Web Server 是 Java 工具集中受欢迎的补充。它不仅使 CLI 托管文件变得非常简单,而且还通过其 API 引入了一些有趣的可能性,特别是与较低级别的HttpServerAPI 结合使用时。