我们在上一篇文章中已经实现了游览器来访问我们自定义的MyServlet,但是静态资源却不能进行访问,这次,我们就实现游览器对静态资源进行访问。
我这次将放一个html页面和一张图片在我们规定的目录下(也就是webapp),代码写好后,最终游览器可以通过发送http请求得到静态资源。
流程梳理 试想一下,我们想让游览器访问静态资源该怎么做呢?我的思路就是使用一个全局的容器(Map)来存储静态资源名字和静态资源的绝对路径。
这样就行了吗?这样肯定不行啊,因为游览器解析文件需要知道MIME类型。我们还要一个配置文件(Properties),里面记录不同后缀名文件的MIME类型。然后程序启动时读取配置文件,存入Map,这样当我们想要返回静态资源时就可以通过后缀名获取到该文件的MIME类型,然后进行返回。
【#|【手写Tomcat】10.实现对静态资源的放行(完结篇)】
文章图片
代码实现
我们在前面创建的MyServletHandler类中增加3个Map来进行存储对应关系。
//这个map用于存放静态资源名字和绝对路径的对应关系
public static final ConcurrentHashMap STATIC_NAME_PATH = new ConcurrentHashMap<>();
//这个map用于存放静态资源名字和ContendType的对应关系
public static final ConcurrentHashMap STATIC_NAME_HEADER = new ConcurrentHashMap<>();
//这个map用于存放扩展名和ContentType的对应关系,在一个静态代码块中加载
public static final ConcurrentHashMap STATIC_SUFFIX_HEADER = new ConcurrentHashMap<>();
创建一个properties文件,里面配置相关的后缀名和MIME的对应关系,这里我就配置了2个用于测试。
文章图片
我们在MyServletHandler类中写一个静态代码块,里面先对properties进行读取,然后对STATIC_SUFFIX_HEADER进行初始化。
static {
Properties properties = new Properties();
try {
//得到项目根路径,并且对路径中的中文进行处理
String path = URLDecoder.decode(Objects.requireNonNull(
MyServletHandler.class.getResource("/")).getPath(), "utf-8");
//加载配置文件
properties.load(new FileInputStream(path+"com/clucky/myTomcat/configuration/MIME.properties"));
//得到我们配置的所有后缀名
Set
这个Map初始化后,我们创建一个方法,initStaticResoure方法,对另外2个Map进行初始。
public static void initStaticResource(){
//读取文件夹
File fileDir = new File("src/webapp");
//得到该文件夹下的所有文件
File[] files = fileDir.listFiles();
assert files != null;
//对文件进行遍历
for (File file : files) {
//判断是否是文件
if (file.isFile()){
//得到文件绝对路径
String absolutePath = file.getAbsolutePath();
//得到名字
String fileName = file.getName();
//得到文件扩展名
String suffixName = fileName.split("\\.")[1];
//判断我们的配置文件中是否配置了这个文件扩展名和MIME的对应关系
if (STATIC_SUFFIX_HEADER.containsKey(suffixName)) {
//放入Map进行存储
STATIC_NAME_PATH.put(fileName,absolutePath);
STATIC_NAME_HEADER.put(fileName,STATIC_SUFFIX_HEADER.get(suffixName));
}
}
}
}
上面,我们初始化Map就写完了。下面我们来对请求分发方面进行完善。我们找到前面写的MyServletThread类。
文章图片
简单回顾一下,上次我们是通过判断来对请求进行分发,但是我们只处理了我们配置的MyServlet,其他都没进行处理,直接返回404。现在我们在if分支中多加一条判断,实现游览器对静态资源的访问。
if (MyServletHandler.STATIC_NAME_PATH.containsKey(uri.substring(1))) {
//如果Map中存有这个静态资源就对游览器进行返回
//得到MIME类型
String contentType = MyServletHandler.STATIC_NAME_HEADER.get(uri.substring(1));
//得到文件的绝对路径
String absolutePath = MyServletHandler.STATIC_NAME_PATH.get(uri.substring(1));
//设置响应类型
response.setContentType(contentType);
//创建一个输入流
InputStream inputStream = new FileInputStream(absolutePath);
//写会游览器
response.write(inputStream);
}
这里我们通过response.write直接进行回写,但是我们在前面定义的write方法就只接收String类型,所以我们在MyHttpServlet接口中定义一个接收inputStream类型的write方法,然后在MyHttpServletImpl类中进行实现。定义接口
文章图片
具体实现
@Override
public void write(InputStream inputStream){
//设置响应头
String header = responseRow+status+"\r\n"+responseHead+contentType;
try {
//写回响应头
outputStream.write(header.getBytes());
byte[] bytes = new byte[1024];
int len = 0;
//写回响应体
while ((len =inputStream.read(bytes))!=-1){
outputStream.write(bytes,0,len);
if (len != bytes.length)break;
}
//关闭流
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
通过上面的处理,我们的代码基本就编写完成了。测试 我在webapp下面放了一个html页面,一张图片,我们通过游览器来进行访问。
webapp目录接口如下
文章图片
html文件和图片的内容如下
文章图片
文章图片
我们启动程序,然后游览器输入http://localhost:8080/login.html,游览器显示了一个html页面,测试成功
文章图片
我们输入http://localhost:8080/ysn.jpg ,游览器显示了一张图片,就是我们放在webapp下的图片,也成功了。
文章图片
总结 通过这10篇手写Tomcat的文章,我们实现了自定义Servlet,自己写Http协议,自定义xml文件,也成功让游览器访问我们自己写的Servlet和静态资源,相信大家在自己写完这些代码后对Tomcat有了一个全新的认识。
以前我们总是觉得tomcat很神秘,tomcat到底是怎样运行的呢,为什么我写了Servlet要在web.xml中就行配置?为什么我在web.xml中配置了Servlet就可以运行了?tomcat怎么就可以对http请求进行分发呢?现在,我们手写了一个迷你的Tomcat,这些算是彻底弄清楚了。原来也就那么会事。
我们写的迷你Tomcat虽然功能不多,但是已经实现了最基本的功能,如获取请求参数,对servlet进行分发,对静态资源进行处理等功能。其他功能都是细节性的了,如果大家感兴趣,可以自己在进行扩展,这里,我给出这次写的所有源代码,欢迎大家下载进行尝试。
百度云链接:提取码 yyds
如果觉得对你有帮助,就关注博主支持一下吧,后面也将会更新更加优质的内容
推荐阅读
- javaWeb|HTTP请求
- #|【手写Tomcat】9.实现游览器访问我们自定义的MyServlet
- #|HTTP基本介绍
- java|safari chrome_Mac用户应放弃Safari的Google Chrome浏览器
- 如何写好 Java 业务代码(这也是有很多规范的..)
- Spring Boot项目微信云托管入门部署
- 国产化之 Arm64 CPU + 银河麒麟系统 安装 .NetCore
- #|基于蒙特卡洛法的规模化电动汽车充电负荷预测(Python&Matlab实现)
- #|一场樱花雨(Python实现)