本文的实例来源 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(不安全)”。就可以看见我们的界面了,如下图:
