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

        我们在上一篇文章中已经实现了游览器来访问我们自定义的MyServlet,但是静态资源却不能进行访问,这次,我们就实现游览器对静态资源进行访问。

        我这次将放一个html页面和一张图片在我们规定的目录下(也就是webapp),代码写好后,最终游览器可以通过发送http请求得到静态资源。


流程梳理

        试想一下,我们想让游览器访问静态资源该怎么做呢?我的思路就是使用一个全局的容器(Map)来存储静态资源名字和静态资源的绝对路径。

        这样就行了吗?这样肯定不行啊,因为游览器解析文件需要知道MIME类型。我们还要一个配置文件(Properties),里面记录不同后缀名文件的MIME类型。然后程序启动时读取配置文件,存入Map,这样当我们想要返回静态资源时就可以通过后缀名获取到该文件的MIME类型,然后进行返回。


 代码实现

        我们在前面创建的MyServletHandler类中增加3个Map来进行存储对应关系。

    //这个map用于存放静态资源名字和绝对路径的对应关系
    public static final ConcurrentHashMap<String,String> STATIC_NAME_PATH = new ConcurrentHashMap<>();
    //这个map用于存放静态资源名字和ContendType的对应关系
    public static final ConcurrentHashMap<String,String> STATIC_NAME_HEADER = new ConcurrentHashMap<>();
    //这个map用于存放扩展名和ContentType的对应关系,在一个静态代码块中加载
    public static final ConcurrentHashMap<String,String> 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<Object> 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类。

         简单回顾一下,上次我们是通过判断来对请求进行分发,但是我们只处理了我们配置的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

        如果觉得对你有帮助,就关注博主支持一下吧,后面也将会更新更加优质的内容

猜你喜欢

转载自blog.csdn.net/m0_51545690/article/details/123306403
今日推荐