手寫簡單版Tomcat(一)——訪問靜態文件

轉載自 https://my.oschina.net/liughDevelop/blog/1790893

作爲一個Java Web開發者,怎麼能不對Tomcat不感興趣呢?

於是,懷着一顆好奇的心,就想要摸一摸Tomcat的底層及實現原理。

那麼,Tomcat究竟在Web中承擔一個什麼樣的角色呢?

首先,當我們在瀏覽器輸入URL 比如http://www.baidu.com 的時候,瀏覽器發送一個Request去獲取 http://www.baidu.com 的html.  Tomcat服務器把Response發送回給瀏覽器。

這個時候,瀏覽器就起到了一個收發Http請求的作用,同時,他還把Response的內容(大部分是HTML)解析成我們可以看的懂的樣式,也就是網頁,具體的我會再寫一篇HTTP協議的博客,分析其中的原理。

Tomcat在這個過程中,會把URL解析,找到對應的資源,再返回給瀏覽器,其中資源可以分爲動態資源和靜態資源。

動態資源就是Java中的Servlet,靜態資源就是html,css,js等無需Tomcat也可以返回給瀏覽器的資源。

也就是:用戶請求-->服務器尋找相應的資源-->服務器輸出對應的資源-->瀏覽器展示給用戶

我們先從簡單的,也就是解析靜態資源開始寫起吧。

代碼詳情看這個https://my.oschina.net/liughDevelop/blog/1790893

我主要是記錄下其中遇到的問題。

首先項目運行起來後,我在瀏覽器輸入http://localhost:8080

結果如下

好像結果不差,那我訪問下文件試試

http://localhost:8080/1.txt

 哦豁,完蛋

這是爲什麼呢?

先看下到底有沒有讀取到本地文件,

輸出下輸入流,看看有沒有讀到

結果看,確實有讀取到本地文件,那既然讀到了,就是沒有成功顯示到瀏覽器上 

 這可就涉及到我的知識盲區了。。。

不過,還是得搞,一個字,盤他!

仔細分析下Response.java裏的這段代碼:

      if (file.exists() && !file.isDirectory()) {
        fis = new FileInputStream(file);
        System.out.println(fis.read());
        int ch = fis.read(bytes, 0, BUFFER_SIZE);
        while (ch!=-1) {
          output.write(bytes, 0, ch);
          System.out.print(new String(bytes, 0, ch));
          ch = fis.read(bytes, 0, BUFFER_SIZE);
        }
      }else {
           //文件不存在,返回給瀏覽器響應提示,這裏可以拼接HTML任何元素
          String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>";
          String returnMessage ="HTTP/1.1 404 File Not Found\r\n" +
                  "Content-Type: text/html\r\n" +
                  "Content-Length: "+retMessage.length()+"\r\n" +
                  "\r\n" +
                  retMessage;
        output.write(returnMessage.getBytes());
      }

返回瀏覽器的代碼就在這

分析可得:

如果文件存在,會運行這段代碼:

        while (ch!=-1) {
          output.write(bytes, 0, ch);
          System.out.print(new String(bytes, 0, ch));
          ch = fis.read(bytes, 0, BUFFER_SIZE);
        }

失敗,會運行這段:

String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>";
          String returnMessage ="HTTP/1.1 404 File Not Found\r\n" +
                  "Content-Type: text/html\r\n" +
                  "Content-Length: "+retMessage.length()+"\r\n" +
                  "\r\n" +
                  retMessage;
        output.write(returnMessage.getBytes());

是不是感覺少了什麼東東?

沒錯!響應頭

OK,加上響應頭後Response.java的代碼如下:

public class Response {
  public static final int BUFFER_SIZE = 2048;
  //瀏覽器訪問D盤的文件
  private static final String WEB_ROOT ="D:";
  private Request request;
  private OutputStream output;

  public Response(OutputStream output) {
    this.output = output;
  }
  public void setRequest(Request request) {
    this.request = request;
  }

  public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    FileInputStream fis = null;
    try {
        //拼接本地目錄和瀏覽器端口號後面的目錄
      File file = new File(WEB_ROOT, request.getUrL());
      //如果文件存在,且不是個目錄
      if (file.exists() && !file.isDirectory()) {
        fis = new FileInputStream(file);
        System.out.println(fis.read());
        int ch = fis.read(bytes, 0, BUFFER_SIZE);
        while (ch!=-1) {
          String retMessage = new String(bytes, 0, ch);;
          retMessage = "HTTP/1.1 200 OK\r\n" +
                  "Content-Type: text/html\r\n" +
                  "Content-Length: "+retMessage.length()+"\r\n" +
                  "\r\n" +
                  retMessage;
          output.write(retMessage.getBytes());
          System.out.print(retMessage);
          ch = fis.read(bytes, 0, BUFFER_SIZE);
        }
      }else {
           //文件不存在,返回給瀏覽器響應提示,這裏可以拼接HTML任何元素
          String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>";
          String returnMessage ="HTTP/1.1 404 File Not Found\r\n" +
                  "Content-Type: text/html\r\n" +
                  "Content-Length: "+retMessage.length()+"\r\n" +
                  "\r\n" +
                  retMessage;
        output.write(returnMessage.getBytes());
      }
    }
    catch (Exception e) {
      System.out.println(e.toString() );
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }
}

 OK,重新訪問下

好吧,雖然亂碼,不過。。。起碼返回是正常的。 

不過這個亂碼始終是不行的,百度了一下發現:

當Java中使用 FileInputStream 讀取txt等文檔時,中文會產生亂碼。

附上解決地址:https://blog.csdn.net/ming2316780/article/details/48443697

修改後Response.java代碼如下:

import java.io.*;


/*
  * Response的功能就是找到這個文件,使用Socket的outputStream把文件作爲字節流輸出給瀏覽器,就可以將我們的HTML顯示給用戶啦
  *
  */
public class Response {
  public static final int BUFFER_SIZE = 2048;
  //瀏覽器訪問D盤的文件
  private static final String WEB_ROOT ="D:";
  private Request request;
  private OutputStream output;

  public Response(OutputStream output) {
    this.output = output;
  }
  public void setRequest(Request request) {
    this.request = request;
  }

  public void sendStaticResource() throws IOException {
    byte[] bytes = new byte[BUFFER_SIZE];
    FileInputStream fis = null;
    try {
        //拼接本地目錄和瀏覽器端口號後面的目錄
      File file = new File(WEB_ROOT, request.getUrL());
      //如果文件存在,且不是個目錄
      if (file.exists() && !file.isDirectory()) {
        fis = new FileInputStream(file);
        InputStreamReader reader = new InputStreamReader(fis,"UTF-8"); //最後的"GBK"根據文件屬性而定,如果不行,改成"UTF-8"試試
        BufferedReader br = new BufferedReader(reader);
        String line;
        while ((line = br.readLine()) != null) {
          String retMessage = "HTTP/1.1 200 OK\r\n" +
                  "Content-Type: text/html;charset=utf-8\r\n" +
                  "Content-Length: "+ line.length() +"\r\n" +
                  "\r\n" +
                  line;
          output.write(retMessage.getBytes());
          System.out.print(retMessage);
        }
      }else {
           //文件不存在,返回給瀏覽器響應提示,這裏可以拼接HTML任何元素
          String retMessage = "<h1>"+file.getName()+" file or directory not exists</h1>";
          String returnMessage ="HTTP/1.1 404 File Not Found\r\n" +
                  "Content-Type: text/html;charset=utf-8\r\n" +
                  "Content-Length: "+retMessage.length()+"\r\n" +
                  "\r\n" +
                  retMessage;
        output.write(returnMessage.getBytes());
      }
    }
    catch (Exception e) {
      System.out.println(e.toString() );
    }
    finally {
      if (fis!=null)
        fis.close();
    }
  }
}

OK,跑起來試下,

 好樣的,沒有亂碼了,不過感覺怪怪的,好像少了點東西

怎麼少了一些字。。。。。。

我滴媽耶,被瀏覽器造了嗎?

 控制檯也輸出的是完整的鴨。。。

難道是Content-Length: 24這個的原因?

OK,把這個刪掉試試

再重新跑下 試試

OJBK,成功! 至於爲什麼Content-Length: 24會影響輸出不完整,我會再寫一篇博客進行分析,如果有哪位童鞋知道,也可以直接評論,博客有什麼不好的地方,歡迎大家指出來,我們一起交流交流。

Over~~~~~~~

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章