HTTP

HTTP

概念

Hyper Text Transfer Protocol :超文本传输协议

传输协议

定义了,客户端和服务器端通信时,发送数据的格式

特点

  1. 基于TCP/IP的高级协议
  2. 默认端口号80
  3. 基于请求/响应模型:一次请求对应一次响应
  4. 无状态的:每次请求之前相互独立,不能交互数据

历史版本

  • 1.0:每一次请求响应都会建立新的连接
  • 1.1:复用连接

请求消息数据格式

  1. 请求行
  2. 请求头
  3. 请求空行
  4. 请求体(正文)
1
2
3
4
5
6
7
8
9
10
11
POST /login.html	HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:60.0) Gecko/20100101 Firefox/60.0
Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Referer: http://localhost/login.html
Connection: keep-alive
Upgrade-Insecure-Requests: 1
# 这里就是请求空行 在请求头和请求体之间的
username=zhangsan

请求行

  • 请求方式(get)
  • 请求url:(/login.html)
  • 请求协议/版本:(HTTP/1.1)
请求方式

HTTP协议有7种请求方式,常用的有2种

  • GET
    • 请求参数在请求行中,在url后
    • 请求的url长度有限制的
    • 不太安全
  • POST
    • 请求参数在请求体中
    • 请求的url长度是没有限制的
    • 相对安全

请求头

客户端浏览器告诉服务器一些信息

请求头名称:请求头值

常见的请求头
  1. User-Agent

    浏览器告诉服务器,我访问你使用的浏览器版本信息,可以在服务端获取该头的信息,解决浏览器的兼容性问题

  2. Referer:http://localhost/login.html

    告诉服务器,当前请求从那里来。

    作用:

    • 防盗链(判断referer是否等于自己网站的地址)
    • 统计工作(判断referer等于那个地址,去计数)

请求空行

空行就是用于分割POST请求的请求头,和请求体的

请求体(正文)

  • 封装POST请求消息的请求参数的

响应消息数据格式

  1. 响应行
  2. 响应头
  3. 响应空行
  4. 响应体
1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP/1.1 200 OK
Content-Type: text/html;charset=UTF-8
Content-Length: 101
Date: Wed, 06 Jun 2018 07:08:42 GMT

<html>
<head>
<title>$Title$</title>
</head>
<body>
hello , response
</body>
</html>

响应行

组成

协议/版本 响应状态码 状态码描述

响应状态码

服务器告诉客户端浏览器本次请求和响应的一个状态

状态码都是3位数字。

分类:

  1. 1xx:服务器接收客户端消息,但没有接收完成,等待一段时间后,发送1xx多状态码
  2. 2xx:成功。代表:200
  3. 3xx:重定向。代表:302(重定向),304(访问缓存)
  4. 4xx:客户端错误。代表:404(请求路径没有对应的资源),405(请求方式没有对应的doXxx方法)
  5. 5xx:服务器端错误。代表:500(服务器内部出现异常)

响应头

格式:头名称:值

常见的响应头
  1. Content-Type:服务器告诉客户端本次响应体数据格式以及编码格式
  2. Content-disposition:服务器告诉客户端以什么格式打开响应体数据。存在以下值
    • in-line:默认值,在当前页面内打开
    • attachment;filename=xxx:以附件形式打开响应体,文件下载

响应空行

响应体

传输的数据

Request

  1. request和response对象是由服务器创建的,我们来使用它们
  2. request对象是用来获取请求消息,response对象是来设置响应消息

image-20230518212118215

request对象继承结构

  • ServletRequest(接口)
    • HttpServletRequest(继承ServletRequest)
      • org.apache.catalina.connector.RequestFacade 类(tomcat,实现HttpServletRequest接口)

request功能

获取请求消息数据

获取请求行数据
  • GET /zhuixun/demo1?name=zhangsan HTTP/1.1

  • 方法

    1. 获取请求方式(GET)

      String getMethod()

    2. 获取虚拟目录 (/zhuixun)

      String getContextPath()

    3. 获取Servlet路径(/demo1)

      String getServletPath()

    4. 获取get方式请求参数(name=zhangsan)

      String getQueryString()

    5. 获取请求URI (代表的范围大于URL)

      • 统一资源标识符(/zhuixun/demo1)
      • String getRequestURI();
    6. 获取请求URL

    7. 获取协议及版本(HTTP/1.1)

      String getProtocol()

    8. 获取客户机的IP地址

      String getRemoteAddr()

获取请求头数据
  • String getHeader(String name)

    通过请求头的名称获取请求头的值

  • EnumerationgetHeaderNmaes()

    获取所有的请求头名称

获取请求体数据

只有POST请求方式,才有请求体,在请求体中封装了POST请求的请求参数

步骤

  1. 获取流对象

    • BufferedReader getReader()

      获取字符输入流,只能操作字符数据

    • ServletInputStream getInputStream()

      获取字节输入流,可以操作所有类型数据(上传文件或图片用到)

  2. 再从流对象中拿数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.io.BufferedReader;
import java.io.IOException;

@WebServlet("/requestDemo5")
public class RequestDemo5 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//获取请求消息体--请求参数
// 1.获取字符流
BufferedReader br = request.getReader();
//2.读取数据
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

}
}

其他功能

获取请求参数的通用方式,不论get还是post请求方式都可以使用下列方法来获取请求参数

  1. String getParameter(String name)

    根据参数名称获取参数值(username=zs&password=123)

  2. String[] getParameterValues(String name)

    根据参数名称获取参数值的数组(hobby=xx&hobby=game)

  3. EnumerationgetParameterNames()

    获取所有请求的参数名称

  4. Map<String,String[]> getParameterMap()

    获取所有参数的map集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.io.IOException;
import java.util.Enumeration;
import java.util.Set;

@WebServlet("/requestDemo6")
public class RequestDemo6 extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//post 获取请求参数
//根据参数名称获取参数值
String username = request.getParameter("username");
/* System.out.println("post");
System.out.println(username);
*/
//根据参数名称获取参数值的数组
String[] hobbies = request.getParameterValues("hobby");
for (String hobby : hobbies) {
System.out.println(hobby);
}
//获取所有请求的参数名称
Enumeration<String> parameterNames = request.getParameterNames();
while (parameterNames.hasMoreElements()) {
String name = parameterNames.nextElement();
System.out.println(name);
String value = request.getParameter(name);
System.out.println(value);
System.out.println("----------------");
}
// 获取所有参数的map集合
Map<String, String[]> parameterMap = request.getParameterMap();
//遍历
Set<String> keyset = parameterMap.keySet();
for (String name : keyset) {
//获取键获取值
String[] values = parameterMap.get(name);
System.out.println(name);
for (String value : values) {
System.out.println(value);
}
System.out.println("-----------------");
}
}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//get 获取请求参数
// 根据参数名称获取参数值
String username = request.getParameter("username");
System.out.println("get");
System.out.println(username);
this.doPost(request, response);
}
}


中文乱码额问题

  • get方式:Tomcat8已经将get方式乱码问题解决了

  • post方式:会乱码

    解决:在获取参数前,设置request的编码

    request.setCharacterEncoding(“utf-8”)

请求转发

一种在服务器内部的资源跳转方式

步骤

  1. 通过request对象获取请求转发器对象

    RequestDispatcher getRequestDispatcher(String path)

  2. 使用RequestDispatcher对象来进行转发

    forward(ServletRequest request,ServletResponse response)

特点

  1. 浏览器地址栏路径不发生变化
  2. 只能转发当前服务器内部资源中
  3. 转发是一次请求

共享数据

只有在转发的情况下才能通过request域去共享数据。

域对象

一个有作用范围的对象,可以在范围内共享数据

request域

代表一次请求的范围,一般用于请求转发的多个资源中共享数据

方法

  1. void setAttribute(String name,Object obj)

    存储数据

  2. Object getAttitude(String name)

    通过键获取值

  3. void removeAttribute(String name)

    通过键移除键值对

  4. 获取ServletContext

    ServletContext getServletContext()

登录案例

  • 用户登录案例需求:
    1. 编写login.html登录页面 username & password 两个输入框
    2. 使用Druid数据库连接池技术,操作mysql,day14数据库中user表
    3. 使用JdbcTemplate技术封装JDBC
    4. 登录成功跳转到SuccessServlet展示:登录成功!用户名,欢迎您
    5. 登录失败跳转到FailServlet展示:登录失败,用户名或密码错误

image-20230518230127547

开发步骤

  1. 创建项目,导入jar包、配置文件

  2. 创建数据库、表

  3. 创建实体类user

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    package com.zhuixun.domain;

    import java.io.Serializable;

    /**
    * @Title: User
    * @Author huan
    * @Package com.zhuixun.domain
    * @Date 2023/5/15 13:57
    * @description:
    */
    public class User implements Serializable {
    private int id;
    private String username;
    private String password;

    public User() {
    }

    public User(int id, String username, String password) {
    this.id = id;
    this.username = username;
    this.password = password;
    }

    public int getId() {
    return id;
    }

    public void setId(int id) {
    this.id = id;
    }

    public String getUsername() {
    return username;
    }

    public void setUsername(String username) {
    this.username = username;
    }

    public String getPassword() {
    return password;
    }

    public void setPassword(String password) {
    this.password = password;
    }

    @Override
    public String toString() {
    return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + '}';
    }
    }
  4. 编写JDBCUtils

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    package com.zhuixun.utils;

    import com.alibaba.druid.pool.DruidDataSourceFactory;

    import javax.sql.DataSource;
    import java.io.FileReader;
    import java.net.URL;
    import java.sql.*;
    import java.util.Properties;

    /**
    * @Title: JdbcUtils
    * @Author huan
    * @Package com.zhuixun.utils
    * @Date 2023/5/15 13:54
    * @description:
    */
    public class JdbcUtils {
    private static String url;
    private static String user;
    private static String password;
    private static String driver;
    private static DataSource ds;

    static {
    try {
    Properties properties = new Properties();
    ClassLoader classLoader = JdbcUtils.class.getClassLoader();
    URL resource = classLoader.getResource("com/zhuixun/dataSource.properties");
    String path = resource.getPath();
    properties.load(new FileReader(path));
    //获取数据库连接属性值
    url = properties.getProperty("url");
    user = properties.getProperty("user");
    password = properties.getProperty("password");
    driver = properties.getProperty("driver");
    //注册驱动
    Class.forName(driver);
    //2.初始化连接池对象
    ds = DruidDataSourceFactory.createDataSource(properties);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    /**
    * 获取连接池对象
    *
    * @return
    */
    public static DataSource getDataSource() {
    return ds;
    }

    /**
    * 获取连接
    *
    * @return 连接对象
    */
    public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(url, user, password);
    }

    /**
    * 释放资源
    *
    * @param stmt
    * @param conn
    */
    public static void close(Statement stmt, Connection conn) {
    if (stmt != null) {
    try {
    stmt.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    if (conn != null) {
    try {
    conn.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    }

    /**
    * 释放资源
    *
    * @param stmt
    * @param conn
    */
    public static void close(ResultSet rs, Statement stmt, Connection conn) {
    if (rs != null) {
    try {
    rs.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    if (stmt != null) {
    try {
    stmt.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    if (conn != null) {
    try {
    conn.close();
    } catch (SQLException e) {
    e.printStackTrace();
    }
    }
    }
    }
  5. 创建userDao,提供对数据库的操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    package com.zhuixun.dao;

    import com.zhuixun.domain.User;
    import com.zhuixun.utils.JdbcUtils;
    import org.springframework.dao.DataAccessException;
    import org.springframework.jdbc.core.BeanPropertyRowMapper;
    import org.springframework.jdbc.core.JdbcTemplate;

    import java.util.List;

    /**
    * @Title: UserDao
    * @Author huan
    * @Package com.zhuixun.dao
    * @Date 2023/5/15 14:33
    * @description:
    */
    public class UserDao {
    private JdbcTemplate jdbcTemplate = new JdbcTemplate(JdbcUtils.getDataSource());

    public User login(User loginUser) {
    try {
    //1.编写sql
    String sql = "select * from user where username = ? and password = ?";
    //2.调用query方法
    User user = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<User>(User.class), loginUser.getUsername(), loginUser.getPassword());
    return user;
    } catch (DataAccessException e) {
    e.printStackTrace();
    //记录日志
    return null;
    }
    }
    }
  6. 编写LoginServlet类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    package com.zhuixun.servlet;

    import com.zhuixun.dao.UserDao;
    import com.zhuixun.domain.User;

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

    /**
    * @Title: LoginServlet
    * @Author huan
    * @Package com.zhuixun.servlet
    * @Date 2023/5/15 13:54
    * @description:
    */
    @WebServlet("/loginServlet")
    public class LoginServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    //1.设置编码
    req.setCharacterEncoding("utf-8");
    //2.获取请求参数
    String username = req.getParameter("username");
    String password = req.getParameter("password");
    //3.封装user对象
    User loginUser = new User();
    loginUser.setUsername(username);
    loginUser.setPassword(password);
    //4.调用UserDao的login方法
    UserDao dao = new UserDao();
    User user = dao.login(loginUser);
    //5.判断user
    if (user == null) {
    //登录失败
    req.getRequestDispatcher("/failServlet").forward(req, resp);
    } else {
    //登录成功
    // 存储数据
    req.setAttribute("user", user);
    //转发
    req.getRequestDispatcher("/successServlet").forward(req, resp);
    }
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    this.doGet(req, resp);
    }
    }
  7. 编写SuccessServlet和FailServlet

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    package com.zhuixun.servlet;

    import com.zhuixun.domain.User;

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

    /**
    * @Title: SuccessServlet
    * @Author huan
    * @Package com.zhuixun.servlet
    * @Date 2023/5/15 16:06
    * @description:
    */
    @WebServlet("/successServlet")
    public class SuccessServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //获取request域中共享的user对象
    User user = (User) request.getAttribute("user");
    if (user != null) {
    //给页面写一句话 设置编码
    response.setContentType("text/html;charset=utf-8");
    //输出
    response.getWriter().write("登录成功!" + user.getUsername() + ",欢迎您");
    }
    }
    }




    package com.zhuixun.servlet;
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;

    @WebServlet("/failServlet")
    public class FailServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //给页面写一句话 //设置编码
    response.setContentType("text/html;charset=utf-8");
    //输出
    response.getWriter().write("登录失败,用户名或密码错误");
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    this.doPost(request, response);
    }
    }
  8. login.jsp

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head><title>$Title$</title></head>
    <body>
    <form action="/loginServlet" method="post"><input name="username"/>
    <input name="password"/>
    <button type="submit">提交</button>
    </form>
    </body>
    </html>

    from表单action路径写法:虚拟目录+Servlet的资源路径

  9. BeanUtils工具类,简化数据封装

    用于封装JavaBean的(导入commons-beanutils.jar)

    • JavaBean:标准的java类
      • 类必须被public修饰
      • 必须提供空参的构造器
      • 成员变量必须使用private修饰
      • 提供公共setter和getter方法

    方法:

    • setProperty()

    • getProperty()

    • populate(Object obj,Map map)

      将map集合的键值对信息,封装到对应的JavaBean对象中。

Response

功能:设置响应消息

设置响应行

  1. 格式(HTTP/1.1 200 ok)

    协议/版本 响应状态码 状态码描述

  2. 设置状态码

    setStatus(int sc)

设置响应头

setHeader(String name, String value)

设置响应体

使用步骤

  1. 获取输出流
    • 字符输出流:PrintWriter getWriter()
    • 字节输出流:ServletOutputStream getOutputStream()
  2. 使用输出流,将数据输出到客户端浏览器

重定向

资源跳转的方式

可以通过

去重定向

1
2
3
4
5
6
7
8
//1.设置状态为302
response.setStatus(302);
//2.设置响应头location
response.setHeader("location","/zhuixun/responseDemo")

//也可以通过这种简单的重定向方法去重定向
response.sendRedirect("/zhuixun/responseDemo");

定向的特点(redirect)

  1. 地址栏发生变化
  2. 重定向可以访问其他站点(服务器)的资源
  3. 重定向是两次请求,不能使用request对象来共享数据

转发的特点(forward)

  1. 转发地址栏路径不变
  2. 转发只能访问当前服务器下的资源
  3. 转发是一次请求,可以使用request对象来共享数据

服务器输出字符数据到浏览器

步骤:

  1. 获取字符输出流
  2. 输出数据

注意:

  1. 乱码问题处理方式

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    //获取流对象之前,设置流的的编码 流的默认编码是ISO-8859-1
    response.setCharacterEncoding("utf-8");

    //告诉浏览器,服务器发送的消息体数据的编码,建议浏览器使用该编码解码,是在获取流之前设置
    response.setHeader("content-type","text/html;charset=utf-8");

    //这是一种简单的设置编码的形式,和上面的话是一个意思
    response.setContentType("text/html;charset=utf-8");

    //获取字符输出流
    PrintWriter pw = response.getWriter();

    //输出数据
    pw.write("你好 response");

服务器输出字节数据到浏览器

步骤:

  1. 获取字节输出流
  2. 输出数据
1
2
3
4
5
6
7
//处理中文乱码
response.setContentType("text/html;charset=utf-8");
//1.获取字节输出流
ServletOutputStream sos = response.getOutputStream();

//2.输出数据
sos.write("hello,你好".getBytes("utf-8"));

验证码

  1. 本质:图片
  2. 目的:防止恶意表单注册
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
package com.zhuiuxn.web.servlet;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
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;

@WebServlet("/checkCodeServlet")
public class CheckCodeServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {


int width = 100;
int height = 50;

//1.创建一对象,在内存中图片(验证码图片对象)
BufferedImage image = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);


//2.美化图片
//2.1 填充背景色
Graphics g = image.getGraphics();//画笔对象
g.setColor(Color.PINK);//设置画笔颜色
g.fillRect(0,0,width,height);

//2.2画边框
g.setColor(Color.BLUE);
g.drawRect(0,0,width - 1,height - 1);

String str = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghigklmnopqrstuvwxyz0123456789";
//生成随机角标
Random ran = new Random();

for (int i = 1; i <= 4; i++) {
int index = ran.nextInt(str.length());
//获取字符
char ch = str.charAt(index);//随机字符
//2.3写验证码
g.drawString(ch+"",width/5*i,height/2);
}


//2.4画干扰线
g.setColor(Color.GREEN);

//随机生成坐标点

for (int i = 0; i < 10; i++) {
int x1 = ran.nextInt(width);
int x2 = ran.nextInt(width);

int y1 = ran.nextInt(height);
int y2 = ran.nextInt(height);
g.drawLine(x1,y1,x2,y2);
}


//3.将图片输出到页面展示
ImageIO.write(image,"jpg",response.getOutputStream());


}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>

<script>
/*
分析:
点击超链接或者图片,需要换一张
1.给超链接和图片绑定单击事件
2.重新设置图片的src属性值

*/
window.onload = function(){
//1.获取图片对象
var img = document.getElementById("checkCode");
//2.绑定单击事件
img.onclick = function(){
//加时间戳,处理浏览器缓存不刷新图片问题
var date = new Date().getTime();
img.src = "/day15/checkCodeServlet?"+date;
}
}
</script>
</head>
<body>
<img id="checkCode" src="/day15/checkCodeServlet" />
<a id="change" href="">看不清换一张?</a>
</body>
</html>

路径写法

路径分类

相对路径

通过相对路径不可以确定唯一资源。

  • 如:./index.html
  • 不以/开头,以.开头路径

规则:找到当前资源和目标资源之间的相对位置关系

  • ./(表示当前目录)
  • ../(后退一级目录)

绝对路径

通过绝对路径可以确定唯一资源

规则:判断定义的路径是给谁用的,判断请求将来从哪儿发出

  • 给客户端浏览器使用:需要加虚拟目录(项目的访问路径)
  • 建议虚拟目录动态获取:request.getContextPath()
  • 给服务器使用,不需要加虚拟目录

ServletContext对象

概念

代表整个web应用,可以和程序的容器(服务器)来通信

获取

  1. 通过request对象获取

    request.getServletContext();

  2. 通过HttpServlet获取

    this.getServletContext()

功能

获取MIME类型

MIME类型:在互联网通信过程中定义的一种文件数据类型

格式:

大类型/小类型 比如:text/html image/jpeg

获取:

String getMimeType(String file)

域对象(共享数据)

  1. setAttribute(String name,Object value)
  2. getAttribute(String name)
  3. removeAttribute(String name)

ServletContext对象范围:所有用户所用请求的数据

获取文件的服务器路径

1
2
3
4
5
6
7
8
9
10
// String getRealPath(String path);
ServletContext context = request.getServletContext();
String b = context.getRealPath("/b.txt");//web目录下资源访问
System.out.println(b);

String c = context.getRealPath("WEB-INF/c.txt");//WEB-INF目录下的资源访问
System.out.println(c);

String a = context.getRealPath("WEB-INF/classes/a.txt");//src目录下的资源访问
System,out.println(a);

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 文件下载需求:
1. 页面显示超链接
2. 点击超链接后弹出下载提示框
3. 完成图片文件下载

分析:
1. 超链接指向的资源如果能够被浏览器解析,则在浏览器中展示,如果不能解析,则弹出下载提示框。不满足需求
2. 任何资源都必须弹出下载提示框
3. 使用响应头设置资源的打开方式(以附件的形式):
* content-disposition:attachment;filename=xxx


步骤:
1. 定义页面,编辑超链接href属性,指向Servlet,传递资源名称filename
2. 定义Servlet
1. 获取文件名称
2. 使用字节输入流加载文件进内存
3. 指定response的响应头: content-disposition:attachment;filename=xxx
4. 将数据写出到response输出流

问题:
* 中文文件问题
* 解决思路:
1. 获取客户端使用的浏览器版本信息
2. 根据不同的版本信息,设置filename的编码方式不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>


<a href="/zhuixun/img/1.jpg">图片1</a>

<a href="/zhuixun/img/1.avi">视频</a>
<hr>


<a href="/zhuixun/downloadServlet?filename=九尾.jpg">图片1</a>

<a href="/zhuixun/downloadServlet?filename=1.avi">视频</a>

</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
package cn.zhuixun.web.utils;

import sun.misc.BASE64Encoder;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;


public class DownLoadUtils {

public static String getFileName(String agent, String filename) throws UnsupportedEncodingException {
if (agent.contains("MSIE")) {
// IE浏览器
filename = URLEncoder.encode(filename, "utf-8");
filename = filename.replace("+", " ");
} else if (agent.contains("Firefox")) {
// 火狐浏览器
BASE64Encoder base64Encoder = new BASE64Encoder();
filename = "=?utf-8?B?" + base64Encoder.encode(filename.getBytes("utf-8")) + "?=";
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
return filename;
}
}



package cn.zhuixun.web.download;

import cn.itcast.web.utils.DownLoadUtils;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;

@WebServlet("/downloadServlet")
public class DownloadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1.获取请求参数,文件名称
String filename = request.getParameter("filename");
//2.使用字节输入流加载文件进内存
//2.1找到文件服务器路径
ServletContext servletContext = this.getServletContext();
String realPath = servletContext.getRealPath("/img/" + filename);
//2.2用字节流关联
FileInputStream fis = new FileInputStream(realPath);

//3.设置response的响应头
//3.1设置响应头类型:content-type
String mimeType = servletContext.getMimeType(filename);//获取文件的mime类型
response.setHeader("content-type",mimeType);
//3.2设置响应头打开方式:content-disposition

//解决中文文件名问题
//1.获取user-agent请求头、
String agent = request.getHeader("user-agent");
//2.使用工具类方法编码文件名即可
filename = DownLoadUtils.getFileName(agent, filename);

response.setHeader("content-disposition","attachment;filename="+filename);
//4.将输入流的数据写出到输出流中
ServletOutputStream sos = response.getOutputStream();
byte[] buff = new byte[1024 * 8];
int len = 0;
while((len = fis.read(buff)) != -1){
sos.write(buff,0,len);
}

fis.close();


}

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
}

HTTP
http://example.com/2023/05/20/JavaWeb/Http/
作者
zhuixun
发布于
2023年5月20日
许可协议