概述
背景
公司内网的服务器层层限制,nginx依赖复杂实在装不上
无奈自己用java做了个nginx,提供负载均衡和静态资源服务
使用java做tcp转发不难,网络上有素材
需要自己补充负载均衡的策略
虽使用springboot也能比较方便的做出静态资源服务器
但是想到负载均衡都做了,干脆整合在一起
于是就有了这个项目,急用可以直接拿
https://github.com/PiaoZhenJia/Jginx
这个小项目的特点是易于安装,易于配置
缺点嘛,肯定是安全和性能啦
这是他的配置文件,上面配置了静态资源服务,下面配置了负载均衡服务
配置文件是个json,并且在首次运行会自动生成配置示例:
{
"un_name_resource_server":{
"listenPort":81,
"mode":"resource",
"path":"D:/",
"uri":"/sample/"
},
"un_name_proxy_server":{
"listenPort":80,
"mode":"proxy",
"remoteLocation":["localhost:8080","localhost:8081","localhost:8082"]
}
}
欢迎使用呀
正文开始
JDK自带了一个简易的HTTP服务器,在包 com.sun.net.httpserver 中
我们现在要用它编写一个静态资源服务器,简单来说,就是拦截一个get请求,并返回对应的文件流。
那么这个 HttpServer 大概是如何工作的呢,我已经总结好了
- 创建HttpServerProvider对象
- 通过HttpServerProvider对象,创建HttpServer对象
- 设置监听的端口
- 设置要拦截的URL和对应的处理类 HttpHandler
- 启动HttpServer
这里的 HttpHandler 是一个接口,我看JDK没有给默认实现,需要我们自己去实现。
先上主类的代码
import com.sun.net.httpserver.HttpServer;
import com.sun.net.httpserver.spi.HttpServerProvider;
import java.io.IOException;
import java.net.InetSocketAddress;
public class Main {
public static void main(String[] args) throws IOException {
HttpServerProvider provider = HttpServerProvider.provider();
HttpServer server = provider.createHttpServer(new InetSocketAddress(80), 1024);//监听80端口
server.createContext("/res/", new HttpGetHandler());// URL 与 Handler 绑定
server.start();
System.out.println("启动好啦");//主类在这里就执行完了,但是因为有监听线程一直运行,所以程序不会退出。
}
}
其中 createHttpServer 方法有两个参数,第一个参数是监听80端口,第二个参数源码是这样说的
/**
* creates a HttpServer from this provider
*
* @param addr
* the address to bind to. May be {@code null}
*
* @param backlog
* the socket backlog. A value of {@code zero} means the systems default
* @throws IOException if an I/O error occurs
* @return An instance of HttpServer
*/
public abstract HttpServer createHttpServer(InetSocketAddress addr,
int backlog)
throws IOException;
我理解是socket的待处理队列长度,填0是系统默认值,这里我们随便填个1024好了
Handler类代码
import com.sun.net.httpserver.Headers;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import java.io.*;
import java.net.URLEncoder;
public class HttpGetHandler implements HttpHandler {
@Override
public void handle(HttpExchange he) throws IOException {
OutputStream responseBody = he.getResponseBody();
String requestMethod = he.getRequestMethod();
Headers responseHeaders = he.getResponseHeaders();
if (requestMethod.equalsIgnoreCase("GET")) {
//url路径替换成文件路径
File file = new File("D:/" + he.getRequestURI().getPath().replace("/res/", ""));
System.out.println("开始下载");
//判断文件是否存在
if (!(file.exists() && file.isFile())) {
responseHeaders.set("Content-Type", "text/html;charset=UTF-8");
responseHeaders.set("Content-Length", "20");
try {
he.sendResponseHeaders(404, 0);
responseBody.write("rn404 File Not Found ~".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
} else {
responseHeaders.set("Content-Type", "applicatoin/octet-stream");
try {
responseHeaders.set("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
responseHeaders.set("Content-Length", String.valueOf(file.length()));
try (InputStream bis = new BufferedInputStream(new FileInputStream(file));
OutputStream ops = new BufferedOutputStream(responseBody)) {
he.sendResponseHeaders(200, 0);
byte[] body = new byte[1024 * 4];//缓存为4kb
int i;
while ((i = bis.read(body)) != -1) {
ops.write(body, 0, i);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
System.out.println("IO连接中断");
}
}
}
try {
responseBody.close();
System.out.println("下载完成");
} catch (IOException e) {
System.out.println("异常停止");
}
}
}
在Handler类中,我们做了静态资源服务器的基本工作,
- 解析URL,将找到磁盘对应路径下的文件,并通过HTTP响应给浏览器下载
- 在判断文件不存在的情况下,会响应404的 http code,和相应简易页面
- 设置了4k的流缓存,防止文件过大时直接读取到内存导致OOM
- 下载时会告知浏览器文件大小(响应头中Content-Length字段)否则浏览器会显示未知大小,影响体验
这是一个最简单的静态资源服务器,他存在以下注意事项
- 安全性不能得到保证,即没有限制路径层级,也不能避免路径穿越
- HttpHandler是同步阻塞执行的,也就是说同时只能处理一个下载
- 一旦HttpHandler的执行抛出了异常,HttpServer便不能再处理其他任何事件
解决方式倒也简单,
- 可以通过正则等方式校验URL
- 在HttpHandler中启动一个线程去执行响应,然后直接返回
- 为HttpHandler添加一个大的 try-catch 块(这个工作其实已经做好了)
简单总结一下服务器为浏览器返回文件流时做的几件事
- 找到文件
- 声明响应头 Content-Type 为 applicatoin/octet-stream
- 在响应头 Content-Disposition 中声明 filename 作为文件名
- 在响应头 Content-Length 中声明文件二进制的长度
- 在响应体中写入二进制流
多线程版本的项目我放在了文初的链接里
此文中仅实现了最简功能,用以帮助同学入手制作Http下载功能。
最后
以上就是激情砖头为你收集整理的用原生Java写一个静态资源服务器的全部内容,希望文章能够帮你解决用原生Java写一个静态资源服务器所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复