typora-copy-images-to: img
request&response
学习目标
第一章-request
知识点-request概述
1.目标
- 知道什么是request以及作用
2.讲解
2.1什么是request
在Servlet API中,定义了一个HttpServletRequest接口,它继承自ServletRequest接口,专门用来封装HTTP请求消息。由于HTTP请求消息分为请求行、请求头和请求体三部分,因此,在HttpServletRequest接口中定义了获取请求行、请求头和请求消息体的相关方法.
Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
2.2request作用
- 操作请求三部分(行,头,体)
- 请求转发
- 作为域对象存数据
3.小结
- request代表请求对象. 原型是HttpServletRequest, 服务器创建好的, 以形参的方式存在doGet()/doPost()方法里面
- Request作用
- 操作请求的三部分(行,头,体)
- 转发
- 作为域对象存取数据
知识点-操作请求行和请求头
1.目标
- 掌握使用request对象获取客户机信息(操作请求行)和获得请求头信息(操作请求头)
2.路径
- 获取客户机信息(操作请求行)
- 获得请求头信息(操作请求头)
3.讲解
3.1获取客户机信息(操作请求行)
请求方式 请求路径(URI) 协议版本
GET /day17Request/WEB01/register.htm?username=zs&password=123456 HTTP/1.1
- getMethod();获取请求方式
- getRemoteAddr() ;获取客户机的IP地址(知道是谁请求的)
- getContextPath(); 获得当前应用工程名(部署的路径);
- getRequestURI();获得请求地址,不带主机名
- getRequestURL();获得请求地址,带主机名
- getServerPort();获得服务端的端口
- getQueryString();获的请求参数(get请求的,URL的?后面的. eg:username=zs&password=123456)
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 08:58
* 获取请求行的信息
*/
@WebServlet("/demo01")
public class ServletDemo01 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request对象获取请求行的信息:
//1. 获取请求的方式(可能会用)
String method = request.getMethod();
//System.out.println("请求方式为:" + method);
//2. 获取客户端的ip地址
String remoteAddr = request.getRemoteAddr();
//System.out.println("客户端的ip地址是:" + remoteAddr);
//3. 获取项目部署的路径(以后可能用到)
String contextPath = request.getContextPath();
//System.out.println("项目部署路径是:" + contextPath);
//4. 获取请求的url: 统一资源定位符 http://localhost:8080/requestDemo/demo01
String url = request.getRequestURL().toString();
//System.out.println("此次请求的url是:" + url);
//5. 获取请求的uri: 统一资源标识符,在url的基础上省略了服务器路径"http://loaclhost:8080"
String uri = request.getRequestURI();
System.out.println(uri);
}
}
3.2.获得请求头信息(操作请求头)
请求头: 浏览器告诉服务器自己的属性,配置的, 以key value存在, 可能一个key对应多个value
getHeader(String name);
- User-Agent: 浏览器信息
- Referer:来自哪个网站(防盗链)
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 09:14
* 使用request获取请求头的信息
*/
@WebServlet("/demo02")
public class ServletDemo02 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//根据请求头的name获取value
//目标:获取name为user-agent的请求头的信息
String header = request.getHeader("user-agent");
System.out.println("获取的请求头agent为:" + header);
}
}
4.小结
- 操作请求行(获得客户机信息)
- getMethod(); 获得请求方式
- getRemoteAddr(); 获得客户机的IP地址
- getContextPath(); 获得项目的部署的路径
- getRequestURI(); 获得URI(不带http,主机,端口)
- getRequestURL(); 获得URL(带http,主机,端口)
- 操作请求头
- getHeader(String name);
- User-Agent: 浏览器信息
- Referer:来自哪个网站(防盗链)
- getHeader(String name);
知识点-操作请求体(获得请求参数)【重点】
1.目标
- 掌握获得请求参数, 以及乱码的解决
2.路径
- 获得请求参数
- 请求参数乱码的处理
- 使用BeanUtils封装请求参数到JavaBean
3.讲解
3.1获得请求参数
法名 | 描述 |
---|---|
String getParameter(String name) | 获得指定参数名对应的值。如果没有则返回null,如果有多个获得第一个。 例如:username=jack |
String[] getParameterValues(String name) | 获得指定参数名对应的所有的值。此方法专业为复选框提供的。 例如:hobby=抽烟&hobby=喝酒&hobby=敲代码 |
Map<String,String[]> getParameterMap() | 获得所有的请求参数。key为参数名,value为key对应的所有的值。 |
3.2 请求参数乱码处理
我们在输入一些中文数据提交给服务器的时候,服务器解析显示出来的一堆无意义的字符,就是乱码。
那么这个乱码是如何出现的呢?如下图所示:
- get方式, 我们现在使用的tomcat>=8.0了, 乱码tomcat已经处理好了
- post方式, 就需要自己处理
void setCharacterEncoding(String env); //设置请求体的编码
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 09:33
* 使用request获取请求参数
*
* 请求参数的乱码问题:
* 在tomcat8之前,无论是get请求还是post请求,请求参数都会发生乱码
* 在tomcat8之后(包含8),只有post请求才会发生中文乱码
*
* 怎么解决乱码: 只要在获取请求参数之前,调用request.setCharacterEncoding(UTF-8);
*/
@WebServlet("/demo03")
public class ServletDemo03 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
//1. 根据参数名,获取一个参数值
String username = request.getParameter("username");
//System.out.println("获取的请求参数username=" + username);
//2. 根据参数名,获取多个参数值:比如说注册时候的兴趣爱好的复选框
String[] hobbies = request.getParameterValues("hobby");
/*for (String hobby : hobbies) {
System.out.println(hobby);
}*/
//3. 获取所有的请求参数
//getParameterMap()获取所有请求参数,请求参数的参数名就是map的key,请求参数的参数值就是map的value
Map<String, String[]> parameterMap = request.getParameterMap();
//遍历出每一个请求参数
Set<Map.Entry<String, String[]>> entries = parameterMap.entrySet();
for (Map.Entry<String, String[]> entry : entries) {
String parameterName = entry.getKey();
String[] values = entry.getValue();
for (String value : values) {
System.out.println("参数名:" + parameterName + ",参数值:" + value);
}
}
}
}
3.3使用BeanUtils封装
现在我们已经可以使用request对象来获取请求参数,但是,如果参数过多,我们就需要将数据封装到对象。
以前封装数据的时候,实体类有多少个字段,我们就需要手动编码调用多少次setXXX方法,因此,我们需要BeanUtils来解决这个问题。
BeanUtils是Apache Commons组件的成员之一,主要用于简化JavaBean封装数据的操作。
使用步骤:
- 导入jar
- 使用BeanUtils.populate(user,map)
- 表单
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/requestDemo/demo04" method="post">
用户名<input type="text" name="username"><br>
密码<input type="text" name="password"><br>
性别<input type="radio" name="gender" value="male">男
<input type="radio" name="gender" value="female">女
<br>
兴趣爱好
<input type="checkbox" name="hobby" value="basketball">篮球
<input type="checkbox" name="hobby" value="football">足球
<input type="checkbox" name="hobby" value="ppball">乒乓球
<input type="checkbox" name="hobby" value="yumaoball">羽毛球
<br>
<input type="submit" value="注册">
</form>
</body>
</html>
- User
/**
* 包名:com.itheima.pojo
*
* @author Leevi
* 日期2020-07-12 10:19
*/
public class User implements Serializable {
private String username;
private String password;
private String gender;
private String[] hobby;
public User() {
}
public User(String username, String password, String gender, String[] hobby) {
this.username = username;
this.password = password;
this.gender = gender;
this.hobby = hobby;
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", password='" + password + '\'' +
", gender='" + gender + '\'' +
", hobby=" + Arrays.toString(hobby) +
'}';
}
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;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String[] getHobby() {
return hobby;
}
public void setHobby(String[] hobby) {
this.hobby = hobby;
}
}
- ServletDemo04
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 10:18
* 我们使用request.getParameterMap()方法获取所有请求参数,是封装在map对象中
*
* 我的需求是将获取到的所有请求参数封装到POJO对象中
* 1. 创建一个POJO类,类中的属性名要和请求参数名对应
* 2. 使用BeanUtils框架,将map中的数据封装到POJO对象中
*/
@WebServlet("/demo04")
public class ServletDemo04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("UTF-8");
//1. 获取所有的请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
//2. 将请求参数封装到User对象中
User user = new User();
//3. 使用BeanUtils框架自动将map中的数据封装到对象中
try {
BeanUtils.populate(user,parameterMap);
System.out.println(user);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4.小结
-
获取请求参数的方法
- getParameter(name),根据参数名获取一个参数值
- getParameterValues(name),根据参数名获取多个参数值
- getParameterMap(),获取所有的请求参数封装到map中
-
使用BeanUtils封装
- 如果请求参数有多个需要封装到JavaBean里面, 建议先获得Map, 再使用BeanUtils封装到JavaBean对象
注意: JavaBean属性需要和Map的key一致 说白了也就是JavaBean属性需要和表单的name一致
-
解决请求参数的中文乱码问题
- Tomcat8之后,使用get方式提交的请求参数不会发生中文乱码
- 解决post请求中的中文参数乱码问题:在获取请求参数之前添加一句代码:request.setCharacterEncoding(“UTF-8”)
知识点-请求转发【重点】
1.目标
- 掌握请求转发
2.讲解
request.getRequestDispatcher(url).forward(request, response);//转发
3.小结
- 请求转发的作用:跳转页面,比如说添加完数据之后跳转到数据的展示页面,删除完数据之后跳转到展示页面
- 请求转发的代码
request.getRequestDispatcher("转发的路径").forward(request,response);
- 请求转发的特征
- 跳转操作是由服务器执行的,所以客户端地址栏不会发生变化
- 跳转操作不会发起新的请求
- 可以跳转到WEB-INF中的资源,但是不能跳转到其它项目的资源
知识点-作为域对象存取值
1.目标
- 掌握requet作为域对象存取值
2.讲解
ServletContext: 范围 整个应用(无论多少次请求,只要是这个应用里面的都是可以共享的)
request范围: 一次请求有效
域对象是一个容器,这种容器主要用于Servlet与Servlet/JSP之间的数据传输使用的。
- Object getAttribute(String name) ;
- void setAttribute(String name,Object object) ;
- void removeAttribute(String name) ;
3.小结
-
作为域对象存取数据
- Object getAttribute(String name) ; 取
- void setAttribute(String name,Object object) ; 存
- void removeAttribute(String name) ; 移除
-
范围: 一次请求有效(转发可以使用)
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 10:50
* request作为域对象在不同的Servlet之间进行数据的共享,它只能在同一次请求中进行数据共享
*
* request域对象只有和请求转发一起使用才有意义
*/
@WebServlet("/demo06")
public class ServletDemo06 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 目标: 在ServletDemo07中获取ServletDemo06中的变量username的值
// 要求只能是由ServletDemo06跳转到ServletDemo07的时候才能获取
String username = "周杰棍";
//将username存储到request域对象中
request.setAttribute("name",username);
//请求转发跳转到ServletDemo07
request.getRequestDispatcher("/demo07").forward(request, response);
}
}
ServletDemo07的代码
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 10:52
*/
@WebServlet("/demo07")
public class ServletDemo07 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//使用request域对象中取出name的值
System.out.println("在ServletDemo07中获取username的值:"+request.getAttribute("name"));
}
}
Request部分的小结
- 获取请求行的信息
- 获取请求方式:getMethod() 掌握
- 获取请求的客户端的ip地址:getRemoteAddr() 掌握
- 获取项目部署的路径:getContextPath() 掌握
- 获取uri:统一资源标识符 掌握
- 获取url:统一资源定位符
- 获取服务器的端口号(了解)
- 获取请求参数的字符串:getQueryString() (了解)
- 获取请求头的信息:getHeader(name) 掌握
- 获取请求参数(全部要掌握,最重要)
- getParameter(name)
- getParameterValues(name)
- getParameterMap()
- 使用BeanUtils将map中的数据存储到JavaBean对象中(掌握)
- map的key要和JavaBean的属性名保持一致,如果不一致那么该字段的值就无法存储
- BeanUtils中默认内置一些基本类型的转换器(如果map中的数据是string类型,JavaBean的属性还是int类型那么会自动转换)
- 使用request做请求转发: request.getRequestDispatcher(“跳转路径”).forward(request,response); 掌握
- request对象作为域对象向存取数据,它的作用范围是一次请求中,和请求转发一起使用 掌握
第二章-Response
知识点-Response概述
1. 目标
- 掌握什么是Response,以及Response的作用
2. 讲解
2.1 HttpServletResponse概述
在Servlet API中,定义了一个HttpServletResponse接口(doGet,doPost方法的参数),它继承自ServletResponse接口,专门用来封装HTTP响应消息。由于HTTP响应消息分为响应行、响应头、响应体三部分,因此,在HttpServletResponse接口中定义了向客户端发送响应状态码、响应头、响应体的方法
2.2作用
- 操作响应的三部分(响应行,响应头,响应体)
3.小结
- Response代表响应对象. 原型是HttpServletResponse, 服务器创建的, 以形参的形式存在doGet()/doPost()方法
- Response的作用
- 操作响应的三部分(行, 头, 体)
知识点-操作响应行
1.目标
- 掌握操作响应行的方法
2.讲解
HTTP/1.1 200
常用的状态码:
200:成功
302:重定向
304:访问缓存
404:客户端错误
500:服务器错误
3.小结
-
设置的API: response.setStatus(int code);
-
一般不需要设置, 可能302 重定向需要设置
-
常见的响应状态码
- 200 成功
- 302 重定向
- 304 读缓存
- 404 客户端错误
- 500 服务器错误
知识点-操作响应头
1.目标
- 掌握操作响应头的方法, 能够进行定时刷新和重定向
2.路径
- 操作响应头的API介绍
- 定时刷新
- 重定向
3.讲解
3.1操作响应头的API
响应头: 是服务器指示浏览器去做什么
一个key对应一个value
一个key对应多个value
关注的方法: setHeader(String name,String value);
常用的响应头
Refresh:定时跳转 (eg:服务器告诉浏览器5s之后跳转到百度)
Location:重定向地址(eg: 服务器告诉浏览器跳转到xxx)
Content-Disposition: 告诉浏览器下载
Content-Type:设置响应内容的MIME类型(服务器告诉浏览器内容的类型)
3.2 定时刷新
response.setHeader("refresh","秒数;url=跳转的路径"); //几秒之后跳转到指定的路径上
3.3 重定向【重点】
- 重定向两次请求
- 重定向的地址栏路径改变
- 重定向的路径写绝对路径(带域名/ip地址的, 如果是同一个项目里面的,域名/ip地址可以省略)
- 重定向的路径可以是项目内部的,也可以是项目以外的(eg:百度)
- 重定向不能重定向到WEB-INF下的资源
- 把数据存到request里面, 重定向不可用
//方式一: 重定向
//1.设置状态码
//response.setStatus(302);
//2.设置重定向的路径(绝对路径,带域名/ip地址的,如果是同一个项目里面的,域名/ip地址可以省略)
//response.setHeader("Location","http://localhost:8080/day28/demo08");
//response.setHeader("Location","/day28/demo08");
//response.setHeader("Location","http://www.baidu.com");
//方式二: 直接调用sendRedirect方法, 内部封装了上面两行
response.sendRedirect("http://localhost:8080/day28/demo08");
- 重定向
response.sendRedirect("重定向的路径");
重定向和请求转发的对比
重定向的特点:
- 重定向的跳转是由浏览器发起的,在这个过程中浏览器会发起两次请求
- 重定向跳转可以跳转到任意服务器的资源,但是无法跳转到WEB-INF中的资源
- 重定向跳转不能和request域对象一起使用
- 重定向跳转浏览器的地址栏中的地址会变成跳转到的路径
请求转发的特点:
1. 请求转发的跳转是由服务器发起的,在这个过程中浏览器只会发起一次请求
2. 请求转发只能跳转到本项目的资源,但是可以跳转到WEB-INF中的资源
3. 请求转发可以和request域对象一起使用
4.小结
4.1操作响应头
知识点-操作响应体
1.目标
- 掌握操作响应体以及响应乱码的解决
2.步骤
- 操作响应体的API介绍
- 响应乱码的解决
3.讲解
操作响应体的API
页面输出只能使用其中的一个流实现,两个流是互斥的.
3.1 使用字符输出流输出字符串
- 解决字符流输出中文乱码问题
response.setContentType("text/html;charset=utf-8");
- 使用字符输出流
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;
import java.io.PrintWriter;
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 12:12
* response设置响应体的信息
* 直接给浏览器要展示的数据
*
* 解决响应体的乱码问题
* response.setContentType("text/html;charset=UTF-8");
* 这句代码底层做了什么?
* 1. 设置服务器响应的字符集为UTF-8
* 2. 设置Content-Type响应头的信息为 "text/html;charset=UTF-8"
* 让浏览器知道了服务器的响应字符集UTF-8,那么浏览器也会使用UTF-8解码
*/
@WebServlet("/demo04")
public class ServletDemo04 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
//要向浏览器输出响应体的信息,需要通过流来进行操作
//第一种:字符串,输出文本内容
PrintWriter writer = response.getWriter();
//使用字符流往浏览器输出文本
//1. writer()方法,只能输出字符串,如果输出int、float等等类型的话,则会有问题
writer.write("你好世界");
//2. print()方法,可以输出数字、字符串
//writer.print(8);
}
}
3.2 使用字节输出流向浏览器输出文件
目标是:向浏览器输出一张图片
注意:需要引入commons-io的jar包
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 14:29
* 使用response的字节流向浏览器输出一个文件(一张图片)
* 目标:浏览器访问ServletDemo05,会在浏览器上显示一张图片
*/
@WebServlet("/demo05")
public class ServletDemo05 extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 读取b.jpg图片,将其转换成字节输入流,使用ServletContext
ServletContext servletContext = getServletContext();
InputStream is = servletContext.getResourceAsStream("b.jpg");
//2. 使用字节输出流,将is中的字节都输出到浏览器
ServletOutputStream os = response.getOutputStream();
/*byte[] buffer = new byte[1024];
int len = 0;
while ((len = is.read(buffer)) != -1){
os.write(buffer,0,len);
}*/
IOUtils.copy(is,os);
os.close();
is.close();
}
}
response部分的总结
- 设置响应状态码:setStatus()
- 设置响应头:setHeader(name,value)
- refresh响应头,用于隔几秒钟之后跳转到某个页面
- location响应头,用于重定向到某个页面
- 重定向的写法: sendRedirect(地址)
- 设置响应体的内容
- 使用字符输出流输出文本内容
- response.getWriter()获取字符输出流
- writer.write()/print()输出字符串
- 解决响应数据的中文乱码:response.setContentType(“text/html;charset=UTF-8”)
- 使用字节输出流输出文件
- response.getOutputStream()获取字节输出流
- 使用字符输出流输出文本内容
- 使用IO流的框架进行边读边写
路径问题
一、完整url地址
url的组成部分:
- 协议 http://
- 服务器主机地址 localhost
- 服务器的端口号 :8080
- 项目的虚拟路径(部署路径) responseDemo
- 具体的项目上资源路径 /pages/hello.html 或者 /demo02 Servlet的映射路径
什么时候会使用完整的url
- 浏览器地址栏直接访问
- 一个项目中,访问另一个项目中的资源
二、相对路径
相对路径的概念
不以"/“开头的路径写法,它是以目标路径相对当前文件的路径,其中”…"表示上一级目录
它是以目标资源的url,相对当前资源的url
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>hello world....</h1>
<!--
目标资源的url: http://localhost:8080/responseDemo/demo05
当前资源的url: http://localhost:8080/responseDemo/pages/demo.html
相对路径的优劣:
1. 优势: 无论部署的项目名怎么改变,我的路径都不需要改变
2. 劣势: 如果当前资源的位置发生改变,那么相对路径就必定要发生改变
-->
<a href="../demo05">访问ServletDemo05</a>
</body>
</html>
三、绝对路径
绝对路径的概念
绝对路径就是以"/"开头的路径写法,它有如下两种情况
- 请求转发的绝对路径写法 “/资源的路径”,不需要写项目路径,例如"/hello.html"
- 不是请求转发的绝对路径写法"/项目部署路径/资源路径" 例如 “/responseDemo/hello.html”
案例-完成文件下载
1.需求分析
- 创建文件下载的列表的页面,点击列表中的某些链接,下载文件.
2.文件下载分析
2.1什么是文件下载
将服务器上已经存在的文件,输出到客户端浏览器.
说白了就是把服务器端的文件拷贝一份到客户端, 文件的拷贝—> 流(输入流和输出流)的拷贝
2.2文件下载的方式
-
第一种:超链接方式(不推荐)
链接的方式:直接将服务器上的文件的路径写到href属性中.如果浏览器不支持该格式文件,那么就会提示进行下载, 如果 浏览器支持这个格式(eg: png, jpg…)的文件,那么直接打开,不再下载了
-
第二种:手动编码方式(推荐)
手动编写代码实现下载.无论浏览器是否识别该格式的文件,都会下载.
3.思路分析
3.1超链接方式
- 准备下载的资源(文件)
- 编写一个下载页面
- 在这个页面上定义超链接,指定href
3.2编码方式
3.2.1手动编码方式要求
设置两个头和一个流
设置的两个头:
Content-Dispostion: 服务器告诉浏览器去下载
Content-Type: 告诉浏览器文件类型.(MIME的类型)
设置一个流:
获得要下载的文件的输入流.
3.2.2思路
4.代码实现
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=utf-8");
//1.获得文件名fileName
String fileName = request.getParameter("fileName");
System.out.println("fileName="+fileName); //eg: a.jpg
//2.设置两头一流
//2.1 一流, 根据文件名获得文件的输入流
InputStream is = getServletContext().getResourceAsStream("download/" + fileName);
//2.2 一头, 告诉浏览器文件的MIME类型(可以不写)
String mimeType = getServletContext().getMimeType(fileName);
response.setHeader("Content-Type",mimeType);
//2.3 一头, 告诉浏览器去下载
response.setHeader("Content-Disposition","attachment;filename="+fileName);
//3.通过response获得输出流(字节流)
OutputStream os = response.getOutputStream();
//4.流的操作
byte[] b = new byte[1024];
int len = 0;
while ( (len=is.read(b))!=-1 ){
os.write(b,0,len);
}
is.close();
os.close();
}
5.细节处理
-
告诉浏览器设置的响应头里面不支持中文的, 抓包来看:
-
解决方案: 手动进行编码再设置进去就ok了
中文文件在不同的浏览器中编码方式不同:火狐是Base64编码, 其它浏览器(谷歌)是URL的utf-8编码
if(agent.contains("Firefox")){
// 火狐浏览器
filename = base64EncodeFileName(filename);
}else{
// IE,其他浏览器
filename = URLEncoder.encode(filename, "UTF-8");
}
public static String base64EncodeFileName(String fileName) {
BASE64Encoder base64Encoder = new BASE64Encoder();
try {
return "=?UTF-8?B?"
+ new String(base64Encoder.encode(fileName
.getBytes("UTF-8"))) + "?=";
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
第三章-综合案例
案例-注册
1. 需求
2. 路径
- 三层结构的讲解
- 完成注册功能
3. 代码实现
3.1 三层架构
- 软件中分层:按照不同功能分为不同层,通常分为三层:表现层(web层),业务层,持久(数据库)层。
- 不同层次包名的命名
分层 | 包名(公司域名倒写) |
---|---|
表现层(web层) | com.itheima.web |
业务层(service层) | com.itheima.service |
持久层(数据库访问层) | com.itheima.dao |
JavaBean | com.itheima.bean |
工具类 | com.itheima.utils |
- 分层的意义:
- 解耦:降低层与层之间的耦合性。
- 可维护性:提高软件的可维护性,对现有的功能进行修改和更新时不会影响原有的功能。
- 可扩展性:提升软件的可扩展性,添加新的功能的时候不会影响到现有的功能。
- 可重用性:不同层之间进行功能调用时,相同的功能可以重复使用。
- 程序设计的宗旨:
- 高内聚低耦合
- 可扩展性强
- 可维护性强
- 可重用性强
3.2 完成注册案例
3.2.1 注册案例思路
3.2.2准备工作
- 数据库
create database day25;
use day25;
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(40) DEFAULT NULL,
`password` varchar(40) DEFAULT NULL,
`address` varchar(40) DEFAULT NULL,
`nickname` varchar(40) DEFAULT NULL,
`gender` varchar(10) DEFAULT NULL,
`email` varchar(20) DEFAULT NULL,
`status` varchar(10) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8
- JavaBean
public class User implements Serializable{
private Integer id;
private String username;
private String password;
private String address;
private String nickname;
private String gender;
private String email;
private String status;//1 表示已激活 0表示未激活
//...
}
- 导入jar
- mysql驱动
- druid
- dbutils
- beanutils
- 工具类和配置文件
- DruidUtil
- druid.properties
3.2.3 注册案例实现
注册页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>注册页面</title>
</head>
<body>
<form action="/userDemo/register" method="post">
用户名<input type="text" name="username"><br>
密码<input type="text" name="password"><br>
昵称<input type="text" name="nickname"><br>
地址<input type="text" name="address"><br>
邮箱<input type="text" name="email"><br>
性别<input type="radio" name="gender" value="male">男
<input type="radio" name="gender" value="female">女
<br>
<input type="submit" value="注册">
</form>
</body>
</html>
RegisterServlet的代码
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 16:15
*/
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//在最前面解决乱码问题:请求参数的中文乱码,响应的中文乱码
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//1. 获取所有的请求参数
Map<String, String[]> parameterMap = request.getParameterMap();
//2. 使用BeanUtils 将parameterMap中的数据,存储到User对象中
User user = new User();
//设置默认的status为"0"
user.setStatus("0");
try {
BeanUtils.populate(user,parameterMap);
//3. 使用DBUtils将用户信息存储到数据库
//这里需要mysql驱动、druid、dbutils的jar包
QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
String sql = "insert into user values (null,?,?,?,?,?,?,?)";
queryRunner.update(sql,user.getUsername(),user.getPassword(),user.getAddress(),
user.getNickname(),user.getGender(),user.getEmail(),user.getStatus());
//如果存储的时候没有出现问题,则说明注册成功,使用重定向跳转到登录页面
response.sendRedirect("/userDemo/login.html");
} catch (Exception e) {
e.printStackTrace();
//如果注册失败,则向浏览器响应一句"注册失败"
response.getWriter().write("注册失败");
}
}
}
4. 小结
- 注册本质: 向数据库插入一条记录
- 思路(在RegisterServlet)
- 获得用户提交的数据, 使用BeanUtils封装成User对象
- 补全User对象(状态)
- 使用DBUtils向数据库里面插入一条记录
- 响应
案例-登录
1.需求
- 点击登录按钮, 进行登录.
- 登录成功,显示login Success
- 登录失败,显示login failed
2.思路
3.代码实现
3,1准备工作
- 页面的准备 login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登录页面</title>
</head>
<body>
<form action="/userDemo/login" method="post">
用户名<input type="text" name="username"><br>
密码<input type="text" name="password"><br>
<input type="submit" value="登录">
</form>
</body>
</html>
3.2代码实现
/**
* 包名:${PACKAGE_NAME}
*
* @author Leevi
* 日期2020-07-12 16:44
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
//1. 解决乱码
request.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
//2. 获取请求参数username和password
String username = request.getParameter("username");
String password = request.getParameter("password");
//3. 连接数据库校验用户名和密码,也就是执行查询的SQL语句
QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
String sql = "select * from user where username=? and password=?";
//执行查询,查询一条数据,封装到User中
User user = queryRunner.query(sql, new BeanHandler<>(User.class), username, password);
//判断是否登录成功
if (user != null) {
//登录成功
//跳转到成功页面success.html
response.sendRedirect("/userDemo/success.html");
}else {
//登陆失败,直接向浏览器输出"登陆失败"
response.getWriter().write("登陆失败");
}
} catch (Exception e) {
e.printStackTrace();
//登陆失败,直接向浏览器输出"登陆失败"
response.getWriter().write("登陆失败");
}
}
}
4.小结
- 本质: 就是根据用户名和密码查询数据库
- 思路(LoginServlet)
- 获得用户输入用户名和密码
- 使用DBUtils根据用户名和密码查询数据库 封装成User对象
- 判断是否登录成功(判断User是否为null)
- 响应
sponse response) throws ServletException, IOException {
try {
//1. 解决乱码
request.setCharacterEncoding(“UTF-8”);
response.setContentType(“text/html;charset=UTF-8”);
//2. 获取请求参数username和password
String username = request.getParameter("username");
String password = request.getParameter("password");
//3. 连接数据库校验用户名和密码,也就是执行查询的SQL语句
QueryRunner queryRunner = new QueryRunner(DruidUtil.getDataSource());
String sql = "select * from user where username=? and password=?";
//执行查询,查询一条数据,封装到User中
User user = queryRunner.query(sql, new BeanHandler<>(User.class), username, password);
//判断是否登录成功
if (user != null) {
//登录成功
//跳转到成功页面success.html
response.sendRedirect("/userDemo/success.html");
}else {
//登陆失败,直接向浏览器输出"登陆失败"
response.getWriter().write("登陆失败");
}
} catch (Exception e) {
e.printStackTrace();
//登陆失败,直接向浏览器输出"登陆失败"
response.getWriter().write("登陆失败");
}
}
}
### 4.小结
1. 本质: 就是根据用户名和密码查询数据库
2. 思路(LoginServlet)
+ 获得用户输入用户名和密码
+ 使用DBUtils根据用户名和密码查询数据库 封装成User对象
+ 判断是否登录成功(判断User是否为null)
+ 响应