java18學習筆記-Simple Web Server

408:Simple Web Server

Python、Ruby、PHP、Erlang 和許多其他平臺提供從命令行運行的開箱即用服務器。這種現有的替代方案表明了對此類工具的公認需求。

提供一個命令行工具來啟動僅提供靜態文件的最小web服務器。沒有CGI或類似servlet的功能可用。該工具將用于原型設計、即席編碼和測試目的,特別是在教育背景下。

Simple Web Server是一個用于服務單個目錄層次結構的最小HTTP服務器。它基于自2006年以來JDK中包含的com.sun.net.httpserver包中的web服務器實現。該包得到了官方支持,我們用API對其進行了擴展,以簡化服務器創建并增強請求處理。Simple Web Server可以通過專用命令行工具jwebserver使用,也可以通過其API以編程方式使用。

以下命令啟動簡單Web服務器

通過jwebserver運行

jwebserver

然后在提示serving的目錄下放一張圖片asd.jpg,然后請求結果如下

注意僅支持 HTTP/1.1。不支持 HTTPS。(但是測試了幾次HTTP/2.0是可以訪問到的)

命令的幾個參數也很簡單

Options:-h or -? or --helpPrints the help message and exits.-b addr or --bind-address addrSpecifies the address to bind to.  Default: 127.0.0.1 or ::1 (loopback).  Forall interfaces use -b 0.0.0.0 or -b ::.-d dir or --directory dirSpecifies the directory to serve.  Default: current directory.-o level or --output levelSpecifies the output format.  none | info | verbose.  Default: info.-p port or --port portSpecifies the port to listen on.  Default: 8000.-version or --versionPrints the version information and exits.To stop the server, press Ctrl + C.

通過JSHELL運行

在Jshell中導入會報錯?sun.net.httpserver.simpleserver.FileServerHandler

import sun.net.httpserver.simpleserver.FileServerHandler;
| ?錯誤:
| ?程序包 sun.net.httpserver.simpleserver 不可見
| ? ?(程序包 sun.net.httpserver.simpleserver 已在模塊 jdk.httpserver 中聲明, 但該模塊未導出它)
| ?import sun.net.httpserver.simpleserver.FileServerHandler;
| ? ? ? ? ^-----------------------------^

所以可以自己復制一個一模一樣的FileServerHandler?

同樣的sun.net.httpserver.simpleserver.ResourceBundleHelper也復制一個

ResourceBundleHelper

import java.text.MessageFormat;
import java.util.Locale;
import java.util.MissingResourceException;
import java.util.ResourceBundle;class ResourceBundleHelper {static final ResourceBundle bundle;static {try {bundle = ResourceBundle.getBundle("sun.net.httpserver.simpleserver.resources.simpleserver");} catch (MissingResourceException e) {throw new InternalError("Cannot find simpleserver resource bundle for locale " + Locale.getDefault());}}static String getMessage(String key, Object... args) {try {return MessageFormat.format(bundle.getString(key), args);} catch (MissingResourceException e) {throw new InternalError("Missing message: " + key);}}
}

復制到Jshell執行

FileServerHandler

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UncheckedIOException;
import java.lang.System.Logger;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.function.UnaryOperator;
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpHandlers;
import static java.nio.charset.StandardCharsets.UTF_8;/*** A basic HTTP file server handler for static content.** <p> Must be given an absolute pathname to the directory to be served.* Supports only HEAD and GET requests. Directory listings and files can be* served, content types are supported on a best-guess basis.*/
public final class FileServerHandler implements HttpHandler {private static final List<String> SUPPORTED_METHODS = List.of("HEAD", "GET");private static final List<String> UNSUPPORTED_METHODS =List.of("CONNECT", "DELETE", "OPTIONS", "PATCH", "POST", "PUT", "TRACE");private final Path root;private final UnaryOperator<String> mimeTable;private final Logger logger;private FileServerHandler(Path root, UnaryOperator<String> mimeTable) {root = root.normalize();@SuppressWarnings("removal")var securityManager = System.getSecurityManager();if (securityManager != null)securityManager.checkRead(pathForSecurityCheck(root.toString()));if (!Files.exists(root))throw new IllegalArgumentException("Path does not exist: " + root);if (!root.isAbsolute())throw new IllegalArgumentException("Path is not absolute: " + root);if (!Files.isDirectory(root))throw new IllegalArgumentException("Path is not a directory: " + root);if (!Files.isReadable(root))throw new IllegalArgumentException("Path is not readable: " + root);this.root = root;this.mimeTable = mimeTable;this.logger = System.getLogger("com.sun.net.httpserver");}private static String pathForSecurityCheck(String path) {var separator = String.valueOf(File.separatorChar);return path.endsWith(separator) ? (path + "-") : (path + separator + "-");}private static final HttpHandler NOT_IMPLEMENTED_HANDLER =HttpHandlers.of(501, Headers.of(), "");private static final HttpHandler METHOD_NOT_ALLOWED_HANDLER =HttpHandlers.of(405, Headers.of("Allow", "HEAD, GET"), "");public static HttpHandler create(Path root, UnaryOperator<String> mimeTable) {var fallbackHandler = HttpHandlers.handleOrElse(r -> UNSUPPORTED_METHODS.contains(r.getRequestMethod()),METHOD_NOT_ALLOWED_HANDLER,NOT_IMPLEMENTED_HANDLER);return HttpHandlers.handleOrElse(r -> SUPPORTED_METHODS.contains(r.getRequestMethod()),new FileServerHandler(root, mimeTable), fallbackHandler);}private void handleHEAD(HttpExchange exchange, Path path) throws IOException {handleSupportedMethod(exchange, path, false);}private void handleGET(HttpExchange exchange, Path path) throws IOException {handleSupportedMethod(exchange, path, true);}private void handleSupportedMethod(HttpExchange exchange, Path path, boolean writeBody)throws IOException {if (Files.isDirectory(path)) {if (missingSlash(exchange)) {handleMovedPermanently(exchange);return;}if (indexFile(path) != null) {serveFile(exchange, indexFile(path), writeBody);} else {listFiles(exchange, path, writeBody);}} else {serveFile(exchange, path, writeBody);}}private void handleMovedPermanently(HttpExchange exchange) throws IOException {exchange.getResponseHeaders().set("Location", getRedirectURI(exchange.getRequestURI()));exchange.sendResponseHeaders(301, -1);}private void handleForbidden(HttpExchange exchange) throws IOException {exchange.sendResponseHeaders(403, -1);}private void handleNotFound(HttpExchange exchange) throws IOException {String fileNotFound = ResourceBundleHelper.getMessage("html.not.found");var bytes = (openHTML+ "<h1>" + fileNotFound + "</h1>\n"+ "<p>" + sanitize.apply(exchange.getRequestURI().getPath()) + "</p>\n"+ closeHTML).getBytes(UTF_8);exchange.getResponseHeaders().set("Content-Type", "text/html; charset=UTF-8");if (exchange.getRequestMethod().equals("HEAD")) {exchange.getResponseHeaders().set("Content-Length", Integer.toString(bytes.length));exchange.sendResponseHeaders(404, -1);} else {exchange.sendResponseHeaders(404, bytes.length);try (OutputStream os = exchange.getResponseBody()) {os.write(bytes);}}}private static void discardRequestBody(HttpExchange exchange) throws IOException {try (InputStream is = exchange.getRequestBody()) {is.readAllBytes();}}private String getRedirectURI(URI uri) {String query = uri.getRawQuery();String redirectPath = uri.getRawPath() + "/";return query == null ? redirectPath : redirectPath + "?" + query;}private static boolean missingSlash(HttpExchange exchange) {return !exchange.getRequestURI().getPath().endsWith("/");}private static String contextPath(HttpExchange exchange) {String context = exchange.getHttpContext().getPath();if (!context.startsWith("/")) {throw new IllegalArgumentException("Context path invalid: " + context);}return context;}private static String requestPath(HttpExchange exchange) {String request = exchange.getRequestURI().getPath();if (!request.startsWith("/")) {throw new IllegalArgumentException("Request path invalid: " + request);}return request;}// Checks that the request does not escape context.private static void checkRequestWithinContext(String requestPath,String contextPath) {if (requestPath.equals(contextPath)) {return;  // context path requested, e.g. context /foo, request /foo}String contextPathWithTrailingSlash = contextPath.endsWith("/")? contextPath : contextPath + "/";if (!requestPath.startsWith(contextPathWithTrailingSlash)) {throw new IllegalArgumentException("Request not in context: " + contextPath);}}// Checks that path is, or is within, the root.private static Path checkPathWithinRoot(Path path, Path root) {if (!path.startsWith(root)) {throw new IllegalArgumentException("Request not in root");}return path;}// Returns the request URI path relative to the context.private static String relativeRequestPath(HttpExchange exchange) {String context = contextPath(exchange);String request = requestPath(exchange);checkRequestWithinContext(request, context);return request.substring(context.length());}private Path mapToPath(HttpExchange exchange, Path root) {try {assert root.isAbsolute() && Files.isDirectory(root);  // checked during creationString uriPath = relativeRequestPath(exchange);String[] pathSegment = uriPath.split("/");// resolve each path segment against the rootPath path = root;for (var segment : pathSegment) {path = path.resolve(segment);if (!Files.isReadable(path) || isHiddenOrSymLink(path)) {return null;  // stop resolution, null results in 404 response}}path = path.normalize();return checkPathWithinRoot(path, root);} catch (Exception e) {logger.log(System.Logger.Level.TRACE,"FileServerHandler: request URI path resolution failed", e);return null;  // could not resolve request URI path}}private static Path indexFile(Path path) {Path html = path.resolve("index.html");Path htm = path.resolve("index.htm");return Files.exists(html) ? html : Files.exists(htm) ? htm : null;}private void serveFile(HttpExchange exchange, Path path, boolean writeBody)throws IOException{var respHdrs = exchange.getResponseHeaders();respHdrs.set("Content-Type", mediaType(path.toString()));respHdrs.set("Last-Modified", getLastModified(path));if (writeBody) {exchange.sendResponseHeaders(200, Files.size(path));try (InputStream fis = Files.newInputStream(path);OutputStream os = exchange.getResponseBody()) {fis.transferTo(os);}} else {respHdrs.set("Content-Length", Long.toString(Files.size(path)));exchange.sendResponseHeaders(200, -1);}}private void listFiles(HttpExchange exchange, Path path, boolean writeBody)throws IOException{var respHdrs = exchange.getResponseHeaders();respHdrs.set("Content-Type", "text/html; charset=UTF-8");respHdrs.set("Last-Modified", getLastModified(path));var bodyBytes = dirListing(exchange, path).getBytes(UTF_8);if (writeBody) {exchange.sendResponseHeaders(200, bodyBytes.length);try (OutputStream os = exchange.getResponseBody()) {os.write(bodyBytes);}} else {respHdrs.set("Content-Length", Integer.toString(bodyBytes.length));exchange.sendResponseHeaders(200, -1);}}private static final String openHTML = """<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>""";private static final String closeHTML = """</body></html>""";private static final String hrefListItemTemplate = """<li><a href="%s">%s</a></li>""";private static String hrefListItemFor(URI uri) {return hrefListItemTemplate.formatted(uri.toASCIIString(), sanitize.apply(uri.getPath()));}private static String dirListing(HttpExchange exchange, Path path) throws IOException {String dirListing = ResourceBundleHelper.getMessage("html.dir.list");var sb = new StringBuilder(openHTML+ "<h1>" + dirListing + " "+ sanitize.apply(exchange.getRequestURI().getPath())+ "</h1>\n"+ "<ul>\n");try (var paths = Files.list(path)) {paths.filter(p -> Files.isReadable(p) && !isHiddenOrSymLink(p)).map(p -> path.toUri().relativize(p.toUri())).forEach(uri -> sb.append(hrefListItemFor(uri)));}sb.append("</ul>\n");sb.append(closeHTML);return sb.toString();}private static String getLastModified(Path path) throws IOException {var fileTime = Files.getLastModifiedTime(path);return fileTime.toInstant().atZone(ZoneId.of("GMT")).format(DateTimeFormatter.RFC_1123_DATE_TIME);}private static boolean isHiddenOrSymLink(Path path) {try {return Files.isHidden(path) || Files.isSymbolicLink(path);} catch (IOException e) {throw new UncheckedIOException(e);}}// Default for unknown content types, as per RFC 2046private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";private String mediaType(String file) {String type = mimeTable.apply(file);return type != null ? type : DEFAULT_CONTENT_TYPE;}// A non-exhaustive map of reserved-HTML and special characters to their// equivalent entity.private static final Map<Integer,String> RESERVED_CHARS = Map.of((int) '&'  , "&amp;"   ,(int) '<'  , "&lt;"    ,(int) '>'  , "&gt;"    ,(int) '"'  , "&quot;"  ,(int) '\'' , "&#x27;"  ,(int) '/'  , "&#x2F;"  );// A function that takes a string and returns a sanitized version of that// string with the reserved-HTML and special characters replaced with their// equivalent entity.private static final UnaryOperator<String> sanitize =file -> file.chars().collect(StringBuilder::new,(sb, c) -> sb.append(RESERVED_CHARS.getOrDefault(c, Character.toString(c))),StringBuilder::append).toString();@Overridepublic void handle(HttpExchange exchange) throws IOException {assert List.of("GET", "HEAD").contains(exchange.getRequestMethod());try (exchange) {discardRequestBody(exchange);Path path = mapToPath(exchange, root);if (path != null) {exchange.setAttribute("request-path", path.toString());  // store for OutputFilterif (!Files.exists(path) || !Files.isReadable(path) || isHiddenOrSymLink(path)) {handleNotFound(exchange);} else if (exchange.getRequestMethod().equals("HEAD")) {handleHEAD(exchange, path);} else {handleGET(exchange, path);}} else {exchange.setAttribute("request-path", "could not resolve request URI path");handleNotFound(exchange);}}}
}

復制到Jshell執行

創建簡單的服務

import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.file.Path;
import java.util.function.UnaryOperator;UnaryOperator<String> identity = UnaryOperator.identity();var server = HttpServer.create(new InetSocketAddress(9000), 10, "/img/",FileServerHandler.create(Path.of("D:\\Program Files"), identity));server.start();

由于在idea中執行放在jshell中執行之后報端口被占用異常,關了idea中的就好了

另外jshell中運行的需要手動自己去找服務停止。

參數解釋

9000? 端口號

10 最大并發數量? <1的話默認會設置成50

/img/? 訪問鏈接前綴

D:\\Program Files 代理到的目標文件,此文件夾下的文件都可以通過http://127.0.0.1:+端口9000+訪問前綴/img/?+文件夾下的文件名(帶后綴)如下

http://127.0.0.1:9000/img/asd.jpg

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/919996.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/919996.shtml
英文地址,請注明出處:http://en.pswp.cn/news/919996.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

深度解析Atlassian 團隊協作套件(Jira、Confluence、Loom、Rovo)如何賦能全球分布式團隊協作

無窮無盡的聊天記錄、混亂不堪的文檔、反饋信息分散在各個不同時區……在全球分布式團隊中開展真正的高效協作&#xff0c;就像是一場不可能完成的任務。 為什么會這樣&#xff1f;因為即使是最聰明的團隊&#xff0c;也會遇到類似的障礙&#xff1a; 割裂的工作流&#xff1a…

理解AI 智能體:智能體架構

1. 引言 智能體架構&#xff08;agent architecture&#xff09;是一份藍圖&#xff0c;它定義了AI智能體各組件的組織方式和交互機制&#xff0c;使智能體能夠感知環境、進行推理并采取行動。本質上&#xff0c;它就像是智能體的數字大腦——整合了“眼睛”&#xff08;傳感器…

Spring Cloud系列—SkyWalking鏈路追蹤

上篇文章&#xff1a; Spring Cloud系列—Seata分布式事務解決方案TCC模式和Saga模式https://blog.csdn.net/sniper_fandc/article/details/149947829?fromshareblogdetail&sharetypeblogdetail&sharerId149947829&sharereferPC&sharesourcesniper_fandc&…

機器人領域的算法研發

研究生期間學習大模型&#xff0c;可投遞機器人領域的算法研發、技術支持等相關崗位&#xff0c;以下是具體推薦&#xff1a; AI算法工程師&#xff08;大模型方向-機器人應用&#xff09;&#xff1a;主要負責大模型開發與優化&#xff0c;如模型預訓練、調優及訓練效率提升等…

深度學習入門:神經網絡

文章目錄一、深度學習基礎認知二、神經網絡核心構造解析2.1 神經元的基本原理2.2 感知器&#xff1a;最簡單的神經網絡2.3 多層感知器&#xff1a;引入隱藏層解決非線性問題2.3.1 多層感知器的結構特點2.3.2 偏置節點的作用2.3.3 多層感知器的計算過程三、神經網絡訓練核心方法…

mysql的索引有哪些?

1. 主鍵索引&#xff08;PRIMARY KEY&#xff09;主鍵索引通常在創建表時定義&#xff0c;確保字段唯一且非空&#xff1a;-- 建表時直接定義主鍵 CREATE TABLE users (id INT NOT NULL,name VARCHAR(50),PRIMARY KEY (id) -- 單字段主鍵 );-- 復合主鍵&#xff08;多字段組合…

【計算機視覺與深度學習實戰】08基于DCT、DFT和DWT的圖像變換處理系統設計與實現(有完整代碼python3.13可直接粘貼使用)

1. 引言 數字圖像處理作為計算機視覺和信號處理領域的重要分支,在過去幾十年中得到了快速發展。圖像變換技術作為數字圖像處理的核心技術之一,為圖像壓縮、特征提取、去噪和增強等應用提供了強有力的數學工具。離散余弦變換(Discrete Cosine Transform, DCT)、離散傅里葉變…

使用Python實現DLT645-2007智能電表協議

文章目錄&#x1f334;通訊支持&#x1f334; 功能完成情況服務端架構設計一、核心模塊劃分二、數據層定義三、協議解析層四、通信業務層&#xff08;以DLT645服務端為例&#xff09;五、通信層&#xff08;以TCP為例&#xff09;使用例子&#x1f334;通訊支持 功能狀態TCP客…

未來已來:基于IPv6單棧隔離架構的安全互聯實踐報告

未來已來&#xff1a;基于IPv6單棧隔離架構的安全互聯實踐報告 報告摘要 隨著IPv4地址資源徹底枯竭&#xff0c;全球網絡基礎設施正加速向IPv6單棧&#xff08;IPv6-Only&#xff09;演進。傳統“IPv4為主、IPv6為輔”的雙棧模式已無法滿足數字化轉型對海量地址、端到端連接與原…

Ubuntu24.04 安裝 Zabbix

Ubuntu24.04 安裝 Zabbix 環境&#xff1a; 軟件版本Ubuntu24.04.3Nginx1.24.0MySQL8.4.6PHP8.3.6phpMyAdmin5.2.2Zabbix7.4.1 LNMP 1. 更新本地軟件包索引并升級已安裝軟件 更新可用軟件包列表 把已安裝的軟件升級到最新版 安裝常用工具 sudo apt update && sud…

【動手學深度學習】6.2. 圖像卷積

目錄6.2. 圖像卷積1&#xff09;互相關運算2&#xff09;卷積層3&#xff09;圖像中目標的邊緣檢測4&#xff09;學習卷積核5&#xff09;互相關與卷積6&#xff09;特征映射和感受野7&#xff09;小結. 6.2. 圖像卷積 卷積神經網絡的設計是用于探索圖像數據&#xff0c;本節…

游戲引擎中的Billboard技術

一.視覺公告板為解決場景中Mesh網格面數過多問題,使用2D平面Mesh替換為3D平面Mesh的技術即為Billboard技術.常用于場景中植被,樹葉,粒子系統等對面數有要求的場景.二.Billboard著色器實現著色器輸入參數:攝像機坐標,網格坐標,攝像機觀察方向著色器輸出:實際2D平面隨視角不變

vue-admin-template權限管理

在基于 vue-admin-template 實現權限管理時&#xff0c;通常需要結合角色權限模型和動態路由機制&#xff0c;以滿足不同用戶角色對頁面訪問權限的控制需求。分為路由頁面權限和按鈕權限&#xff1a;下面是具體實現思路的思維導圖和具體代碼流程&#xff1a;0.實現邏輯思維導圖…

微信小程序,事件總線(Event Bus) 實現

1、util.js文件/*** 事件總線*/ function createEventBus() {// 私有事件存儲對象&#xff0c;通過閉包保持私有性const events {};return {/*** 監聽事件&#xff0c;只執行一次* param {string} eventName - 事件名稱* param {Function} callback - 回調函數*/once(eventNam…

OpenCV結構光三維重建類cv::structured_light::GrayCodePattern

操作系統&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 編程語言&#xff1a;C11 算法描述 cv::structured_light::GrayCodePattern 是 OpenCV 庫中用于結構光三維重建 的一個類&#xff0c;屬于 OpenCV 的 structured_light 模塊。 它用于…

變頻器實習DAY35 引腳電平測試 退耦電阻

目錄變頻器實習DAY35一、工作內容1.1 硬性平臺RO7測試二、學習內容2.1 退耦電阻核心原理&#xff1a;2大特性抑制干擾四大關鍵作用選型&#xff1a;4個核心參數典型應用場景四大常見誤區附學習參考網址歡迎大家有問題評論交流 (* ^ ω ^)變頻器實習DAY35 一、工作內容 1.1 硬性…

C++標準庫算法:從零基礎到精通

算法庫的核心理念與設計哲學 C標準庫算法的設計遵循著一個令人稱道的哲學&#xff1a;算法與容器的分離。這種設計并非偶然&#xff0c;而是經過深思熟慮的結果。傳統的面向對象設計可能會將排序功能綁定到特定的容器類中&#xff0c;但C標準庫卻選擇了一條更加優雅的道路——…

為什么存入數據庫的中文會變成亂碼

從產生、傳輸、處理到最終存儲的整個生命周期中采用統一且正確的字符集編碼。具體原因紛繁復雜&#xff0c;主要歸結為&#xff1a;客戶端操作系統或應用與數據庫服務端字符集編碼不一致、Web應用服務器到數據庫驅動的連接層編碼配置缺失或錯誤、數據庫本身及其表、字段各層級的…

13種常見機器學習算法面試總結(含問題與優質回答)

目錄 1. K近鄰&#xff08;K-NN&#xff09; 2. 線性回歸&#xff08;一元/多元&#xff09; 3. 邏輯回歸 4. 決策樹 5. 集成學習之隨機森林 6. 貝葉斯&#xff08;樸素/高斯&#xff09; 7. SVM&#xff08;支持向量機&#xff09; 8. K-means聚類 9. DBSCAN 10. TF-…

sfc_os!SfcValidateFileSignature函數分析之WINTRUST!SoftpubLoadMessage

第一部分&#xff1a;0: kd> kc# 00 WINTRUST!SoftpubLoadMessage 01 WINTRUST!_VerifyTrust 02 WINTRUST!WinVerifyTrust 03 sfc_os!SfcValidateFileSignature 04 sfc_os!SfcGetValidationData 05 sfc_os!SfcValidateDLL 06 sfc_os!SfcQueueValidationThread 07 kernel32!B…