Javaweb-Http、Maven及Servlet详解

一、Http

1.什么事Http

Http(超文本传输协议)是一个简单的请求-响应协议,它通常运行在TCP之上
  • 文本:html,字符串,~..

  • 超文本:图片,音乐,视屏,定位,地图...

  • 端口为80
    Https:安全的,端口号443

2.两个时代

  • http1.0

    • HTTP/1.0:客户端可以与web服务器连接后,只能获得一个web资源,断开连接
  • http2.0

    • HTTP/1.1:客户端可以与web服务器连接后,可以获得多个web资源

3.Http请求

  • 客户端--发请求(requset)--服务器
    百度:
Request URL: https://www.baidu.com/    请求地址
Request Method: GET      get方法/post方法
Status Code: 200 OK      状态码:200
Remote(远程地址) Address: 39.156.66.14:443
Accept: text/html
Accept-Language: zh-CN,zh;q=0.9
Cache-Control: max-age=0
Connection: keep-alive

(1)请求行

  • 请求行中的请求方式:get

  • 请求方式:Get,Post

    • Get:请求能够携带的参数比较少,大小有限制,会在浏览器的URL地址栏显示数据内容,不安全,但高效
    • Postget:请求能够携带的参数没有限制,大小没有限制,不会在浏览器的URL地址栏显示数据内容,安全,但不高效

(2)消息头

Accept: 告诉浏览器,他所支持的数据类型
Accept-Language: zh-CN,zh;q=0.9,告诉浏览器,它的语言环境
Cache-Control: max-age=0: 缓存控制
Connection: keep-alive:告诉浏览器,请求完成是断开还是保持连接
HOST:主机

4.Http响应

  • 服务器--响应--客户端

百度:

Cache-Control: private   
Connection: keep-alive
Content-Encoding: gzip
Content-Type: text/html;charset=utf-8

(1)响应体

Accept: 告诉浏览器,他所支持的数据类型
Accept-Language: zh-CN,zh;q=0.9,告诉浏览器,它的语言环境
Cache-Control: max-age=0: 缓存控制
Connection: keep-alive:告诉浏览器,请求完成是断开还是保持连接
HOST:主机
Refresh:告诉客户端,多久刷新一次;
Location:让网页重新定位

(2)响应状态码

200:请求响应成功

3xx:请求重定向

  • 重定向:你重新到我给你新位置去;

4xx:找不到资源

  • 资源不存在

5xx:服务器代码错误 500 ,502:网关错误
常见面试题:
当你的浏览器中地址栏输入地址并回车的一瞬间到页面能够展示回来,经历了什么?

二、Maven

为什么要学习这个技术?
  • 在Javaweb开发中,需要使用大量的jar包,我们手动去导入

  • 如何能够让一个东西自动帮我导入和配置这个jar包,由此,Maven诞生了

1.Maven项目架构管理工具

我们目前用来就是方便导入jar包的 Maven的核心思想:约定大于配置
  • 有约束,不要去违反
    Maven会规定好你该如何去编写我们的Java代码,必须要按照这个规范来;

2.下载安装Maven

下载链接

https://maven.apache.org/download.cgi

3.配置Maven环境变量

Mac终端输入
vim ~/.bash_profile

然后将以下配置加入里面

MAVEN_HOME=/Users/twq/Downloads/apache-maven-3.8.6
PATH=$MAVEN_HOME/bin:$PATH
M2_HOME=/Users/twq/Downloads/apache-maven-3.8.6/bin
export MAVEN_HOME
export PATH
export M2_HOME

最后输入 mvn -v
出现如下图表名配置成功


Windows:
在环境变量中添加M2_home和MAVEN_HOME两个路径


并继续添加PATH的路径

4.配置阿里云镜像

  • 镜像:mirrors

    • 作用:加速我们的下载
  • 国内建议使用阿里云的镜像

<mirror> 
        <id>nexus-aliyun</id> 
        <mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf> 
        <name>Nexus aliyun</name> 
        <url>http://maven.aliyun.com/nexus/content/groups/public/</url> </mirror> 

将此配置放入setting.xml的mirrors中即可

5.设置本地仓库

创建一个本地仓库
<localRepository>/Users/twq/Downloads/apache-maven-3.8.6/maven-repo</localRepository

6.在IDEA中操作Maven


进来后可能还会显示pom.xml(Unknown)
我的第一次进来是这样,不知道为啥我的pom.xml里是空白的
最后自己手动添加了如下

<?xml version="1.0" encoding="UTF-8" ?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example </groupId>
    <!--项目名-->
    <artifactId>tang</artifactId>
    <version>1.0-SNAPSHOT</version>

</project>

进入后如果没有自动下载maven依赖,则可以自己在IDEA的终端输入以下命令下载

mvn dependency:resolve-plugins

最后出现如下图所示则表明下载成功


成功后,自己建的maven仓库里就有了很多文件

(1)新建src相关目录

而且还没有src目录在,找了很多解决方法,最后发现如下解决办法 ①点击项目右键新建目录


这里会自动显示你想要的src以及test文件


创建完成如下图所示

(2)新建webapp目录

(3)将IDEA中的文件标记为指定样式的文件

方法一:


方法二:

(4)配置Tomcat

①解决警告问题
为什么会有这个警告:我们访问一个网站需要指定一个文件夹名字

启动Tomcat会直接显示webapp里面的index.jsp文件里的内容

(5)pom.xml文件

<?xml version="1.0" encoding="UTF-8" ?>

<!--maven的版本和头文件-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example </groupId>
    <artifactId>tang</artifactId>
    <version>1.0-SNAPSHOT</version>
    <!-- Package:项目打包的方式
        jar:Java应用
        war:javaweb应用
    -->
<!--    <packaging>war</packaging>-->

<!--    &lt;!&ndash;  配置  &ndash;&gt;-->
<!--    <properties>-->
<!--        &lt;!&ndash;项目默认构建编码&ndash;&gt;-->
<!--        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>-->
<!--        &lt;!&ndash;编译版本&ndash;&gt;-->
<!--        <maven.compiler.source>1.8</maven.compiler.source>-->
<!--        <maven.compiler.target>1.8</maven.compiler.target>-->
<!--    </properties>-->
    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.22</version>
        </dependency>

    </dependencies>
<!--    在build中配置resource,防止我们资源导出失败的问题-->
       <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>
</project>

(6)maven的jar包结构图

三、Servlet

1.Servlet简介

  • Servlet就是sun公司开发动态web的一门技术

  • Sun在这些API中提供一个接口叫做:Servelt,如果你想开发一个Servlet程序,只需要完成两个小步骤:

    • 编写一个类,实现servlet接口
    • 把开发好的Java类部署到web服务器中

把实现了Servlet接口的Java程序叫做Servlet

2.关于Maven父子工程的理解

父项目中会有
<!--指明该父类下的子模块-->
    <modules>
        <module>Servlet-01</module>
    </modules>

子项目中会有

<!--    子类必须指明父类-->
    <parent>
        <artifactId>javaweb-02-Servlet</artifactId>
        <groupId>org.example</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>

父项目中的Java,子项目可以直接使用

注意:

父项目的打包方式必须为pom且必须要定义打包方式,子项目的打包方式必须为jar,否则会报错:Invalid packaging for parent POM org.example:javaweb-02-Servlet:1.0-SNAPSHOT (/Users/twq/Downloads/javaweb-02-Servlet/pom.xml), must be "pom" but is "jar"

3.编写HelloServlet

(1).Maven仓库的使用

官网地址 https://mvnrepository.com/

Tomcat中Servlet的使用样例链接
http://localhost:8080/examples/servlets/


上图若没有搜索到,则可去Maven仓库中寻找


一搜发现搜到一堆没用的,心想既然在Tomcat上能使用肯定是导入了某一个与HttpServlet相关的jar包,于是乎又来到Tomcat的lib目录(存放各种jar包的地方)下查找


发现lib目录下与servlet相关的jar包只有一个,于是再次回到maven仓库中寻找该jar包


我选择的是导入maven依赖,复制上图方框里的依赖代码,依赖里的provided表示作用范围,把这行去掉


然后将Tomcat中Servlet下的hello样例代码粘贴过来,就是如下代码

response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");

(2).编写web.xml

建一个包

①编写servlet的映射
为什么需要映射:我们写的是Java程序,但是要通过浏览器访问,而浏览器需要连接web服务器,所以我们需要在web服务中注册我们写的Servlet,还需要给他一个浏览器能够访问的路径
<!--注册servlet-->
    <servlet>
        <!--随便起个名字-->
        <servlet-name>hello</servlet-name>
        <servlet-class>com.tang.HelloServlet</servlet-class>
    </servlet>

<servlet-mapping>
        <servlet-name>hello</servlet-name>
        <!--请求路径 -->
        <url-pattern>/Twq</url-pattern>
 </servlet-mapping>
②Servlet映射原理

可以根据输入的指定路径如我的指定的Twq路径http://localhost:8080/Servlet_01_war/Twq,然后根据Twq请求路径在Servlet-mapping中找到servlet-name也即是hello,然后在根据注册的servlet-name找到要执行的Java程序,最后运行Java程序

自定义前后缀映射

①指定前缀,后缀随意
 <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <!--请求路径 -->
        <url-pattern>/Twq/*</url-pattern>
    </servlet-mapping>

测试数据:
http://localhost:8080/Servlet_01_war/Twq/fafsdafgsfvdsfsdgg
测试结果:

②指定后缀,前缀随意
 <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <!--请求路径 -->
        <!--注意点 .*前面不能加项目映射的路径否则会启动Tomcat报错 -->
        <url-pattern>*.Twq</url-pattern>
    </servlet-mapping>

测试数据:
http://localhost:8080/Servlet_01_war/sdfadfvfsgsddfrqw.Twq
测试结果:
发现只要后缀是.Twq结尾前面是什么无所谓

web.xml代码

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
<!--        web.xml中是配置我们web的核心应用-->
    <!--注册servlet-->
    <servlet>
        <!--随便起个名字-->
        <servlet-name>helloServlet</servlet-name>
        <servlet-class>com.tang.HelloServlet</servlet-class>
    </servlet>
    <!--一个servlet对应一个mapping:映射-->
    <servlet-mapping>
        <servlet-name>helloServlet</servlet-name>
        <!--请求路径 -->
        <url-pattern>/Twq</url-pattern>
    </servlet-mapping>
</web-app>

4.运行Tomcat

(1)解决启动报错

然后运行Tomcat可能会报 java: 错误: 不支持发行版本 5这个错误(之前还能用的,不知道怎么回事突然报了这个错误,说实在的这两天解决这些奇怪的报错,真是让人头疼!!!) 解决方法(没办法,还是得笑对人生继续改错)

完成上面几步之后可以解决此问题,Tomcat就可以正常启动了
运行结果图

(2)解决中文乱码

乱码的原因及解决方法

解决方法:
设置浏览器的响应的编码格式即可

HelloServlet代码

package com.tang;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse response) throws ServletException, IOException {
        //响应的类型:html
        response.setContentType("text/html");
        //设置浏览器响应的编码格式
        response.setCharacterEncoding("UTF-8");
        //获取响应的输出流
        PrintWriter out = response.getWriter();

        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>唐昊!!</h1>");
        out.println("</body>");
        out.println("</html>");

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }

}

5.ServletContext对象

web容器在启动的时候,它会为每个web程序都创建一个对应的ServletContext对象,它代表了当前的web应用;

(1).共享数据

  • 在这个Servlet中保存的数据,可以咋另外一个Servlet中拿到;
    存放数据的类

      public class HelloServlet extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          //响应的类型:html
          ServletContext context = this.getServletContext();
    
          String username="唐三";
          context.setAttribute("username",username); //将一个数据保存在了ServletContext中,名字为:username,值为username
    
      }
    }
    

    读取的类

      public class GetServlet extends HttpServlet {
      @Override
      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          ServletContext context = this.getServletContext();
          String username = (String) context.getAttribute("username");
          resp.setContentType("text/html");
          resp.setCharacterEncoding("UTF-8");
        //        resp.setHeader("text/html","UTF-8");
          resp.getWriter().println("姓名:"+username);
      }
    
      @Override
      protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    
      }
    }
    

    web.xml

      <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee   http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
           version="4.0">
      <servlet>
          <servlet-name>hello</servlet-name>
          <servlet-class>com.tang.HelloServlet</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>hello</servlet-name>
          <url-pattern>/Twq1</url-pattern>
      </servlet-mapping>
    
      <servlet>
          <servlet-name>get</servlet-name>
          <servlet-class>com.tang.GetServlet</servlet-class>
      </servlet>
      <servlet-mapping>
          <servlet-name>get</servlet-name>
          <url-pattern>/Twq2</url-pattern>
      </servlet-mapping>
    </web-app>
    

    测试访问结果:

(2)获取初始化参数

web.xml代码如下
<context-param>
        <param-name>url</param-name>
        <param-value>jdbc:mysql://localhost:3306</param-value>
    </context-param>
 <servlet>
        <servlet-name>gp</servlet-name>
        <servlet-class>com.tang.ServletDemo03</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>gp</servlet-name>
        <url-pattern>/gp</url-pattern>
    </servlet-mapping>

ServletDemo03中代码如下

package com.tang;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ServletDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();

        String url = context.getInitParameter("url");

        resp.setContentType("text/html");
        resp.setCharacterEncoding("UTF-8");

        resp.getWriter().println(url);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

(3)请求转发

ServletDemo04代码

package com.tang;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ServletDemo04 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext context = this.getServletContext();

        System.out.println("进入了ServletDemo04");
        RequestDispatcher requestDispatcher = context.getRequestDispatcher("/gp");//转发的请求路径
        requestDispatcher.forward(req,resp);//调用forward实现请求转发

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

web.xml 代码

    <servlet>
        <servlet-name>sd4</servlet-name>
        <servlet-class>com.tang.ServletDemo04</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>sd4</servlet-name>
        <url-pattern>/sd4</url-pattern>
    </servlet-mapping>

运行结果图


转发的大致思想:
比如有A,B,C,现在有一个需求是A想要拿到C的资源,但是A又无法直接调用C,且只能连接到B,就只能B找C拿,然后C在返回给B,最后B在将C返回的数据交给A,这个过程中,A和C始终没有见到C,所以上图路径没有变化(用sd4的路径去访问jp路径里面的内容),图形化过程如下:

(4)读取资源文件

Properties
  • 在Java目录下新建Properties

  • 在resource目录下新建Properties

发现:都被打包到了同一个路径下:classes,我么俗称这个路径为classpath:
当在java目录下写资源文件时,并不会被打包,可以通过如下方法解决,将以下代码放入当前模块的pom.xml中即可

    <build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>true</filtering>
            </resource>
        </resources>
    </build>

加上之后再重新打包,就会生成资源文件


开始读取该资源文件的流
aa.properties文件里的内容如下:

username=root
password=123456

web.xml代码

<servlet>
        <servlet-name>sd5</servlet-name>
        <servlet-class>com.tang.ServletDemo05</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>sd5</servlet-name>
        <url-pattern>/sd5</url-pattern>
    </servlet-mapping>

ServletDemo05代码如下:

package com.tang;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class ServletDemo05 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        InputStream resource = this.getServletContext().getResourceAsStream("/WEB-INF/classes/com/tang/aa.properties");

        Properties properties = new Properties();

        properties.load(resource);

        String username = properties.getProperty("username");
        String password = properties.getProperty("password");

        resp.getWriter().println(username+":"+password);

    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

当前项目路径


运行结果图

6.HttpServletResponse

响应:web服务器就收到客户端的Http请求,针对这个请求,分别创建一个代表请求的HttpServletRequest对象,代表响应的一个HttpServletResponse;
  • 如果要获取客户端请求过来的参数:找HttpSerletRequest

  • 如果要给客户端响应一些信息:找HttpServletResponse

(1)简单分类

①负责像浏览器发送数据的方法
ServletOutputStream getOutputStream() throws IOException;

PrintWriter getWriter() throws IOException;

②负责向浏览器发送响应头的方法

void setDateHeader(String var1, long var2);

    void addDateHeader(String var1, long var2);

    void setHeader(String var1, String var2);

    void addHeader(String var1, String var2);

    void setIntHeader(String var1, int var2);

    void addIntHeader(String var1, int var2);

(2)常见应用

①下载文件(了解)
  • 要获取下载文件的路径

  • 下载的文件名是啥?

  • 设置想办法让浏览器能够支持下载我们需要的东西

  • 获取下载文件的输入流

  • 输出获取到的输入流

    • 创建缓冲区
    • 获取OutputStream对象
    • 将FileOutputStream流写入到buffer缓冲区中
    • 使用OutputStream将缓冲区中的数据输出到客户端
      web.xml代码如下:
<servlet>
            <servlet-name>fileDown</servlet-name>
            <servlet-class>com.tang.FileServlet</servlet-class>
        </servlet>
    <servlet-mapping>
        <servlet-name>fileDown</servlet-name>
        <url-pattern>/down</url-pattern>
    </servlet-mapping>

该模块的pom.xml中尽量加上如下代码:

<build>
        <resources>
            <resource>
                <directory>src/main/resources</directory>
                <excludes>
                    <exclude>**/*.properties</exclude>
                    <exclude>**/*.xml</exclude>
                </excludes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
    </build>

FileServlet代码如下

package com.tang;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URLEncoder;

public class FileServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp){
        FileInputStream in = null;//将文件变成流
        ServletOutputStream outputStream = null;
        try {
            //1.要获取下载文件的路径
            String realPath = "/Users/twq/Downloads/javaweb-02-Servlet/out/artifacts/Servlet_03_war_exploded/WEB-INF/classes/唐三.jpg";
            System.out.println("下载的文件的路径为:"+ realPath);
            //2.下载的文件名是啥?
            String fileName = realPath.substring(realPath.lastIndexOf("\\") + 1);// 因为一个/需要转义,所以写两个表示截取到最后一个/,然后加1就表示最后一个/后面的名字,也即是文件名
            //3.设置想办法让浏览器能够支持下载我们需要的东西
            //Content-disposition:表示web下载文件的头消息,也是用其来支持下载我们需要的东西;中文文件名使用URLEncoder.encode编码,否则可能会乱码
            resp.setHeader("Content-disposition","attachment;filename="+ URLEncoder.encode(fileName,"UtF-8"));
            //4.获取下载文件的输入流
            in = new FileInputStream(realPath);
            //5.输出获取到的输入流
            //6.创建缓冲区
            int len = 0;
            byte[] buffer = new byte[1024];
            //7.获取OutputStream对象
            outputStream = resp.getOutputStream();
            //8.将FileOutputStream流写入到buffer缓冲区中,使用OutputStream将缓冲区中的数据输出到客户端
            while((len=in.read(buffer)) != -1){
                outputStream.write(buffer,0,len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭流
            if(in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(outputStream != null){
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

填写文件的绝对路径时,可以再out目录下去复制对应文件的绝对路径

运行结果图

②验证码功能(了解)
验证实现方法
  • 前端实现

  • 后端实现,需要用到Java的图片类,生成一个图片
    web.xml注册代码

 <servlet>
        <servlet-name>ImageServlet</servlet-name>
        <servlet-class>com.tang.ImageServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>ImageServlet</servlet-name>
        <url-pattern>/img</url-pattern>
    </servlet-mapping>

ImageServlet代码

package com.tang;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;

public class ImageServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //让浏览器3秒自动刷新一次
        resp.setHeader("refresh","2");

        //在内存中创建一个图片
        BufferedImage image = new BufferedImage(100, 20, BufferedImage.TYPE_INT_RGB);
        //得到图片,用一个2D的笔
        Graphics2D g = (Graphics2D)image.getGraphics();
        //设置图片的背景颜色
        g.setColor(Color.white);
        //fillRect:填充一个矩形,从(0,0)点开始到(80,20)结束填充一个背景为白色矩形
        g.fillRect(0,0,100,20);
        //给图片写数据
        g.setColor(Color.red);//设置画笔颜色
        g.setFont(new Font(null,Font.BOLD,20));//设置字体
        g.drawString(makeNum(),0,20);//在(0,20)这个位置将随机数给画上去

        //告诉浏览器,这个请求用图片的方式打开
        resp.setContentType("image/jpg");
        //网站存在缓存,设置不让浏览器缓存
        resp.setDateHeader("Expires",-1);
        resp.setHeader("Cache-Control","no-cache");
        resp.setHeader("Pragma","no-cache");

        //把图片写给浏览器
        ImageIO.write(image,"jpg",resp.getOutputStream());
    }
    //生成随机数
    private String makeNum(){
        Random random = new Random();
        //7个9表示一个七位数,一个整数加上一个空串之后变为一个字符串
        String num = random.nextInt(9999999) + "";
        StringBuffer sb = new StringBuffer();
        //保证生成的数为七位的,不足七位补零
        for(int i =0; i < 7-num.length();i++){
            sb.append("0");
        }
        num = sb.toString() + num;
        return num;
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

运行结果图

③重定向(重点)
一个web资源(B)收到客户端(A)请求,他(B)会通知客户端(A)去访问另一个web资源(C),这个过程叫重定向


常见场景:

  • 用户登录
    web.xml代码
<servlet>
        <servlet-name>Redirect</servlet-name>
        <servlet-class>com.tang.RedirectServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Redirect</servlet-name>
        <url-pattern>/red</url-pattern>
    </servlet-mapping>

RedirectServlet代码

package com.tang;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RedirectServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /*
          由运行结果下面结果图知下面这行代码等价于
          resp.setHeader("Location","/Servlet_03_war/img");  
          resp.setStatus(302);
        */
        resp.sendRedirect("/Servlet_03_war/img");//重定向
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

运行结果图


注意:重定向的路径要添加如下图所示的东西

重定向和转发的区别(面试题)

相同点:
  • 页面都会实现跳转

不同点:

  • 请求转发的时候,转发的url不会发生变化

  • 重定向的时候,url地址栏会发生变化

7.HttpServletRequest

HttpServletRequest代表客户端的请求,用户通过Http协议访问服务器,HTTP请求中的所有信息会被封装到HttServletRequest,通过这个HttpServletRequest的方法获得客户端的所有信息

(1)获取参数,请求转发


RequestTest代码如下:

package com.tang;

import javax.naming.Context;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class RequestTest extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("有请求进来");
        //处理请求
        String username = req.getParameter("username");//获取请求参数的值
        String password = req.getParameter("password");

        System.out.println(username + ":" +password);
        //重定向的时候一定添加web应用的路径,否则404
        //resp.sendRedirect("/Servlet_03_war/Success.jsp");
        /*转发的时候web应用的路径就不用写了,否则也会报404
        因为这里的 / 代表的就是当前的web应用,在写就重复了*/
        req.getRequestDispatcher("/Success.jsp").forward(req,resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

web.xml代码

<servlet>
        <servlet-name>request</servlet-name>
        <servlet-class>com.tang.RequestTest</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>request</servlet-name>
        <url-pattern>/login</url-pattern>
    </servlet-mapping>

index.jsp代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<%--这里提交的路经,需要寻找到项目的路径--%>
<%--${pageContext.request.contextPath}代表当前的项目--%>
<form action="${pageContext.request.contextPath}/login" method="get">
    用户名:<input type="text" name="username"><br>
    密码:<input type="text" name="password"><br>
    <input type="submit">
</form>
</body>
</html>

Success.jsp代码

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>Success</h1>
</body>
</html>

运行结果

8.Cookie、Session

(1)会话

会话:用户从打开一个浏览器->点击了很多超链接->访问多个web资源->关闭浏览器,这个过程可以称之为会话。

有状态的会话:一个同学来过教室下次再来教室,我们会知道这个同学曾经来过,称之为有状态会话

一个网站,怎么证明你来过?

  • 服务端给客户端一个信件,客户端下次访问服务端带上信件就可以了;cookie

  • 服务器登记你来过了,下次你来的时候我来匹配你;session

(2)保存会话的两种技术

①cookie
  • 客户端技术(服务器通过响应将数据发给客户端,客户端通过请求将数据带到服务器上)
    CookieDemo代码
package com.tang.cookie;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;

//保存用户上一次访问的时间
public class CookieDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //服务器告诉你,你来的时间,把这个时间封装成一个信件,你下次带来,我就知道你来了

        //解决中文乱码
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html");
        PrintWriter out = resp.getWriter();

        //Cookie,服务器端从客户端获取
        Cookie[] cookies = req.getCookies();//这里返回数组,说明Cookie可能存在多个

        //判断Cookie是否存在
        if(cookies != null){
            //如果存在的话,就遍历取出就行
            out.write("你上一次访问的时间是:");

            for (int i = 0; i < cookies.length; i++) {
                Cookie cookie = cookies[i];
                //获取cookie的名字
                if(cookie.getName().equals("lastLoginTime")){
                    //获取cookie中的值
                    long l = Long.parseLong(cookie.getValue());//将一个字符串类型的时间戳转换为长整型数据
                    Date date = new Date(l);//将时间戳转化为日期
                    out.write(date.toLocaleString());//将一个日期转化为字符串,并写出
                     //解码
                    //out.write(URLDecoder.decode(cookie.getValue(),"utf-8"));
               
                }
            }
        }else{
            out.write("这是您第一次访问本站");
        }

        //服务器给客户端响应一个cookie,System.currentTimeMillis()得到的是从1970年1月1日到今天所获得的时间戳,单位为毫秒
        //编码:中文传输尽量这样传,以防乱码
        //Cookie cookie = new Cookie("name", URLEncoder.encode("唐昊","utf-8"));
       
        Cookie cookie = new Cookie("lastLoginTime", System.currentTimeMillis()+"");
        resp.addCookie(cookie);//该方法需要一个Cookie所以就new一个就行
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

web.xml中servlet的注册代码就不写了,上面写的带太多变了
运行结果图


并且当你退出浏览器,再次进入后cookie值就会消失,但是如果设置cookie的生存期的话,即使退出浏览器,cookie的值任然存在,设置生存期的方法

//cookie有效期为1天,里面存储的单位是秒,因此要计算一天有多少秒
 cookie.setMaxAge(24*60*60);

cookie的细节问题:

  • 一个cookie只能保存一个信息;

  • 一个web站点可以给浏览器发送多个cookie,最多存放20个cookie;

  • cookie大小有限制为4kb

  • 300个cookie浏览器上限

删除Cookie

  • 不设置有效期,关闭浏览器,自动失效

  • 设置有效期时间为0;

编码解码:

//编码:中文传输尽量这样传,以防乱码
Cookie cookie = new Cookie("name", URLEncoder.encode("唐昊","utf-8"));
//解码
out.write(URLDecoder.decode(cookie.getValue(),"utf-8"));
②session(重点)
  • 服务器技术,利用这个技术,可以保存用户的会话信息,我们可以吧信息或者数据放在Session中

  • 服务器会给每一个用户(浏览器)创建一个Session对象;

  • 一个Session独占一个浏览器,只要浏览器没有关闭,这个session就存在

  • 用户登录之后,整个网站它都可以访问 -->保存用户的信息:保存购物车的信息....
    Session 和Cookie的区别:

  • Cookie是把用户的数据写给用户的浏览器,浏览器保存(可以保存多个)

  • Session把用户的数据写到用户独占Session中,服务器端保存(保存重要的信息,减少服务器资源的浪费)

  • Session对象由服务创建
    使用Session
    SessionDemo代码如下

package com.tang.session;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;

public class SessionDemo extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决乱码问题
        resp.setCharacterEncoding("UTF-8");
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html");

        //得到session
        HttpSession session = req.getSession();

        //给Session中存东西
        session.setAttribute("name",new Person("唐三",23));

        //获取Session的ID
        String sessionId = session.getId();

        //判断session是不是新创建的
        if(session.isNew()){
            resp.getWriter().write("session创建成功,ID为:"+sessionId);
        }else{
            resp.getWriter().write("session在服务器中已经存在了,ID为:"+sessionId);

        }

        //Session创建的时候自动做了如下的事情
//        Cookie cookie = new Cookie("JSESSIONID",sessionId);
//        resp.addCookie(cookie);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

获取Session里面存的数据
SessionDemo02

package com.tang.session;

import javax.servlet.ServletException;
import javax.servlet.http.*;
import java.io.IOException;

public class SessionDemo02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //解决乱码问题
        resp.setCharacterEncoding("UTF-8");
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html");

        //得到session
        HttpSession session = req.getSession();

        Person person = (Person) session.getAttribute("name");
        System.out.println(person.toString());
        resp.getWriter().write(person.toString());
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

运行结果图

手动注销Session
注:注销的原理就是将其SessionID给注销,注销后再次访问会分配一个新的SessionID

package com.tang.session;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

public class SessionDemo03 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        HttpSession session = req.getSession();
        session.removeAttribute("name");
        //手动注销Session
        session.invalidate();
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doGet(req, resp);
    }
}

会话自动失效:web.xml代码,注册的代码就不展示了

<!--    设置Session默认失效时间-->
    <session-config>
<!--        1分钟后,Session失效,以分钟为单位-->
        <session-timeout>1</session-timeout>
    </session-config>
③常见场景
网站登录之后,你下次不用在登录了,第二次范文就直接访问进去
posted @ 2022-07-26 21:59  剑断青丝ii  阅读(796)  评论(0编辑  收藏  举报