#|【手写Tomcat】10.实现对静态资源的放行(完结篇)

我们在上一篇文章中已经实现了游览器来访问我们自定义的MyServlet,但是静态资源却不能进行访问,这次,我们就实现游览器对静态资源进行访问。
我这次将放一个html页面和一张图片在我们规定的目录下(也就是webapp),代码写好后,最终游览器可以通过发送http请求得到静态资源。
流程梳理 试想一下,我们想让游览器访问静态资源该怎么做呢?我的思路就是使用一个全局的容器(Map)来存储静态资源名字和静态资源的绝对路径。
这样就行了吗?这样肯定不行啊,因为游览器解析文件需要知道MIME类型。我们还要一个配置文件(Properties),里面记录不同后缀名文件的MIME类型。然后程序启动时读取配置文件,存入Map,这样当我们想要返回静态资源时就可以通过后缀名获取到该文件的MIME类型,然后进行返回。
【#|【手写Tomcat】10.实现对静态资源的放行(完结篇)】#|【手写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个用于测试。
#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片

我们在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 set = properties.keySet(); //对我们得到的所有后缀名进行遍历 for (Object suffix : set) { //得到MIME类型 Object mime = properties.get(suffix); //存入Map STATIC_SUFFIX_HEADER.put((String) suffix, (String) mime); } } catch (IOException e) { System.out.println("读取properties配置文件出错"); } }
这个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类。
#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片

简单回顾一下,上次我们是通过判断来对请求进行分发,但是我们只处理了我们配置的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类中进行实现。
定义接口
#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片
具体实现
@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目录接口如下
#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片

html文件和图片的内容如下
#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片

#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片

我们启动程序,然后游览器输入http://localhost:8080/login.html,游览器显示了一个html页面,测试成功
#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片

我们输入http://localhost:8080/ysn.jpg ,游览器显示了一张图片,就是我们放在webapp下的图片,也成功了。
#|【手写Tomcat】10.实现对静态资源的放行(完结篇)
文章图片

总结 通过这10篇手写Tomcat的文章,我们实现了自定义Servlet,自己写Http协议,自定义xml文件,也成功让游览器访问我们自己写的Servlet和静态资源,相信大家在自己写完这些代码后对Tomcat有了一个全新的认识。
以前我们总是觉得tomcat很神秘,tomcat到底是怎样运行的呢,为什么我写了Servlet要在web.xml中就行配置?为什么我在web.xml中配置了Servlet就可以运行了?tomcat怎么就可以对http请求进行分发呢?现在,我们手写了一个迷你的Tomcat,这些算是彻底弄清楚了。原来也就那么会事。
我们写的迷你Tomcat虽然功能不多,但是已经实现了最基本的功能,如获取请求参数,对servlet进行分发,对静态资源进行处理等功能。其他功能都是细节性的了,如果大家感兴趣,可以自己在进行扩展,这里,我给出这次写的所有源代码,欢迎大家下载进行尝试。
百度云链接:提取码 yyds
如果觉得对你有帮助,就关注博主支持一下吧,后面也将会更新更加优质的内容

    推荐阅读