本文的实例来源 HttpComponents 官网,源代码地址 https://hc.apache.org/httpcomponents-core-ga/httpcore/examples/org/apache/http/examples/HttpFileServer.java ,本文仅仅是将源程序运行成功。要运行程序需要如下两步:
(1)生成我们自己的证书。在 dos 窗口运行如下命令:
keytool -genkey -validity 36000 -alias www.hxstrive.com -keyalg RSA -keystore my.keystore
上面命令中,证书有效期36000天,别名为 www.hxstrive.com ,算法为 RSA,将生成的证书放到当前目录下面的 my.keystore 文件。
注意:在生成证书时,你需要记住秘钥库和秘钥的密码,因为下面程序中将会使用。程序中将秘钥库和秘钥密码均设置为“secret”。
(2)将源码放到IDEA中进行运行。项目结构如下图:
上图中,将 my.keystore 存放到 resources 目录,即 classpath 下面。然后创建一个 document/download/demo.html 文件,我们将通过浏览器浏览该 html 文件。程序源代码如下:
package org.apache.http.examples; import java.io.File; import java.io.IOException; import java.net.SocketTimeoutException; import java.net.URL; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.Locale; import java.util.concurrent.TimeUnit; import javax.net.ssl.SSLContext; import org.apache.http.ConnectionClosedException; import org.apache.http.ExceptionLogger; import org.apache.http.HttpConnection; import org.apache.http.HttpEntity; import org.apache.http.HttpEntityEnclosingRequest; import org.apache.http.HttpException; import org.apache.http.HttpRequest; import org.apache.http.HttpResponse; import org.apache.http.HttpStatus; import org.apache.http.MethodNotSupportedException; import org.apache.http.config.SocketConfig; import org.apache.http.entity.ContentType; import org.apache.http.entity.FileEntity; import org.apache.http.entity.StringEntity; import org.apache.http.impl.bootstrap.HttpServer; import org.apache.http.impl.bootstrap.ServerBootstrap; import org.apache.http.protocol.HttpContext; import org.apache.http.protocol.HttpCoreContext; import org.apache.http.protocol.HttpRequestHandler; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; /** * Embedded HTTP/1.1 file server based on a classic (blocking) I/O model. * 基于经典(阻塞)I/O模型的嵌入式HTTP/1.1文件服务器。 * * 如果端口为 8080,非8443端口,则需要使用 https:// 协议访问。例如: * https://localhost:8080/demo.html * * 如果端口为 8443,则需要使用 https:// 协议访问。例如: * https://localhost:8443/demo.html */ public class HttpFileServer { public static void main(String[] args) throws Exception { String docRoot = System.getProperty("user.dir") + "\\document\\download"; int port = 8443; // 开启SSL,使用 https:// 协议访问 System.out.println("根目录:" + docRoot); System.out.println("端 口:" + port); SSLContext sslContext = null; if (port == 8443) { // Initialize SSL context URL url = HttpFileServer.class.getResource("/my.keystore"); if (url == null) { System.out.println("Keystore not found"); System.exit(1); } sslContext = SSLContexts.custom() // 证书地址,秘钥库密码,秘钥密码 .loadKeyMaterial(url, "secret".toCharArray(), "secret".toCharArray()) .build(); } SocketConfig socketConfig = SocketConfig.custom() .setSoTimeout(15000) .setTcpNoDelay(true) .build(); final HttpServer server = ServerBootstrap.bootstrap() .setListenerPort(port) .setServerInfo("Test/1.1") .setSocketConfig(socketConfig) // 添加SSL上下文 .setSslContext(sslContext) // 添加错误处理器 .setExceptionLogger(new StdErrorExceptionLogger()) // 注册处理器 .registerHandler("*", new HttpFileHandler(docRoot)) .create(); // 添加关闭钩子,JVM退出时调用去关闭 server Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { server.shutdown(5, TimeUnit.SECONDS); System.out.println("server already shutdown"); } }); // 启动服务 server.start(); server.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // 等待终止 } /** * 错误处理器 */ static class StdErrorExceptionLogger implements ExceptionLogger { @Override public void log(final Exception ex) { if (ex instanceof SocketTimeoutException) { System.err.println("Connection timed out"); } else if (ex instanceof ConnectionClosedException) { System.err.println(ex.getMessage()); } else { ex.printStackTrace(); } } } /** * Http文件处理器 */ static class HttpFileHandler implements HttpRequestHandler { private final String docRoot; public HttpFileHandler(final String docRoot) { super(); this.docRoot = docRoot; } public void handle( final HttpRequest request, final HttpResponse response, final HttpContext context) throws HttpException, IOException { String method = request.getRequestLine().getMethod().toUpperCase(Locale.ROOT); if (!method.equals("GET") && !method.equals("HEAD") && !method.equals("POST")) { throw new MethodNotSupportedException(method + " method not supported"); } String target = request.getRequestLine().getUri(); // 获取请求行URI if (request instanceof HttpEntityEnclosingRequest) { HttpEntity entity = ((HttpEntityEnclosingRequest) request).getEntity(); byte[] entityContent = EntityUtils.toByteArray(entity); System.out.println("Incoming entity content (bytes): " + entityContent.length); } final File file = new File(this.docRoot, URLDecoder.decode(target, "UTF-8")); if (!file.exists()) { // 文件不存在 response.setStatusCode(HttpStatus.SC_NOT_FOUND); StringEntity entity = new StringEntity( "<html><body><h1>File " + file.getPath() + " not found</h1></body></html>", ContentType.create("text/html", "UTF-8")); response.setEntity(entity); System.out.println("File " + file.getPath() + " not found"); } else if (!file.canRead() || file.isDirectory()) { // 文件不能读,或是一个目录 response.setStatusCode(HttpStatus.SC_FORBIDDEN); StringEntity entity = new StringEntity( "<html><body><h1>Access denied</h1></body></html>", ContentType.create("text/html", "UTF-8")); response.setEntity(entity); System.out.println("Cannot read file " + file.getPath()); } else { // 返回文件内容 HttpCoreContext coreContext = HttpCoreContext.adapt(context); HttpConnection conn = coreContext.getConnection(HttpConnection.class); response.setStatusCode(HttpStatus.SC_OK); FileEntity body = new FileEntity(file, ContentType.create("text/html", (Charset) null)); response.setEntity(body); System.out.println(conn + ": serving file " + file.getPath()); } } } }
运行效果如下图:
如果你使用的时 8443 端口,则开启SSL。此时,我们需要使用 https:// 协议访问。如下图:
由于我们的证书是自签名的,浏览器报不安全。点击“高级”按钮,如下图:
然后,选择“继续前往localhost(不安全)”。就可以看见我们的界面了,如下图: