Web是全球广域网,也称为万维网(www),能够通过浏览器访问的网站。
JavaWeb就是用Java技术来解决相关web互联网领域的技术栈。
B/S 架构:Browser/Server,浏览器/服务器 架构模式
它的特点是:客户端只需要浏览器,应用程序的逻辑和数据都存储在服务器端
B/S架构的好处: 易于维护升级。服务器端升级后,客户端无需任何部署就可以使用到新的版本
动态资源主要包含Servlet、JSP等,主要用来负责逻辑处理。
Servlet
- Servlet 是 Java 编写的服务器端程序
- 它们接收来自客户端(通常是浏览器)的请求,处理请求,生成动态内容,并将结果发送回客户端
- Servlet 可以处理各种类型的请求和响应,如 HTML、XML、JSON 等
- Servlet 提供了灵活性和控制权,允许程序员直接处理请求和生成响应
JSP
JSP 是一种基于 Java 的服务器端技术,允许开发者在 HTML 页面中嵌入 Java 代码
JSP 页面被服务器翻译成 Servlet,然后由 Servlet 容器运行
JSP 提供了一种简化创建动态 Web 内容的方式,因为它允许将 Java 代码嵌入到 HTML 中,使得页面的开发更加方便
JSP in HTML Example Welcome to My Website
This is a simple example of using JSP in HTML.
<%-- JSP code to get the current date and time --%> <% java.util.Date currentDate = new java.util.Date(); %>Current Date and Time: <%= currentDate %>
展示效果:
动态资源处理完逻辑后会把**得到的结果交给静态资源**来进行展示,动态资源和静态资源要结合一起使用
整个Web的访问过程就如下图所示:
(1)浏览器发送一个请求到服务端,去请求所需要的相关资源;
(2)资源分为动态资源和静态资源,动态资源可以是使用Java代码按照Servlet和JSP的规范编写的内容;
(3)在Java代码可以进行业务处理也可以从数据库中读取数据;
(4)拿到数据后,把数据交给HTML页面进行展示,再结合CSS和JavaScript使展示效果更好;
(5)服务端将静态资源响应给浏览器;
(6)浏览器将这些资源进行解析;
(7)解析后将效果展示在浏览器,用户就可以看到最终的结果。
可扩展 三个字表面上的意思是XML允许自定义格式。但这不代表你可以随便写。
XML基本语法这个知识点的定位是:我们不需要从零开始,从头到尾的一行一行编写XML文档,而是在第三方应用程序、框架已提供的配置文件的基础上修改。要改成什么样取决于你的需求,而怎么改取决XML基本语法和具体的XML约束。
主要作用:配置信息
<configuration>
<database>
<host>localhosthost>
<port>3306port>
<username>adminusername>
<password>secretpasswordpassword>
database>
<appSettings>
<theme>lighttheme>
<language>en_USlanguage>
appSettings>
configuration>
回顾:常见的配置文件类型
- properties文件,例如druid连接池就是使用properties文件作为配置文件
- XML文件,例如Tomcat就是使用XML文件作为配置文件
- YAML文件,例如SpringBoot就是使用YAML作为配置文件
- json文件,通常用来做文件传输,也可以用来做前端或者移动端的配置文件
- 等等…
XML的基本语法和HTML的基本语法简直如出一辙。
XML命名规则
1、名称可以包含字母、数字以及其他的字符。
2、名称不能以数字或者标点符号开始。
3、名称不能以字母 xml(或者 XML、Xml 等等)开始。
4、名称不能包含空格。
5、可使用任何名称,没有保留的字词。
<first_name>、<last_name>
XML声明
XML元素
XML 元素指的是从(且包括)开始标签直到(且包括)结束标签的部分
<books>
<book>
<name>三国演义name>
<author>罗贯中author>
<price>39.9price>
<version>1.0version>
book>
books>
XML注释
XML中的注释和html中的注释的写法是一样的
XML属性
书写在标签内的。对标签的数据进行扩展。对标签的进一步描述
<标签名 属性名=“属性值” 属性名=“属性值”> 标签名>
CDATA区
CDATA区:可以输出特殊字符:原样的显示书写在CDATA的内容。会原封不动的显示出去。
转义表达 | 符号 | 意义 |
---|---|---|
< |
< | 小于 |
> |
> | 大于 |
& |
& | 和号 |
&apos ; |
’ | 单引 |
" ; |
“” | 双引 |
示例:
<books>
<book>
<name>西游记name>
<author sex="男" address="郑州"><吴承恩>author>
<pirce>50pirce>
<version>1.2version>
book>
books>
效果:
dom4j
示例:
创建user.xml
<users>
<user id="10001" country="Chinese" source="Android">
<id>10001id>
<name>adminname>
<password>111111password>
user>
<user id="10002" country="Chinese" source="ios">
<id>10002id>
<name>tonyname>
<password>666666password>
user>
users>
创建Dom4jParseUserXmlTest类,获取user.xml中相关信息:
package com.baidu;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.util.List;
public class Dom4jParseUserXmlTest {
public static void main(String[] args) {
//1、创建解析器对象
SAXReader saxReader = new SAXReader();
//2、使用解析器对象读取XML文档生成Document对象
try {
Document document = saxReader.read(Dom4jParseUserXmlTest.class.getClassLoader().getResource("user.xml"));
//3、根据Document对象获取XML的元素标签信息
/**
* 1、org.dom4j.Document常用方法
* Element getRootElement(); 获取XML文件的根节点
* 2、org.dom4j.Element常用方法
* String getName();返回
* Listelements();获取标签的子标签
*/
//3.1 获取XML文件的根节点
Element rootElement = document.getRootElement();
System.out.println("user.xml文件的根节点的名字为"+rootElement.getName());
//3.2 获取XML文件的根节点下的子节点
System.out.println("获取根标签users的子标签列表");
List<Element> usersSubElementList = rootElement.elements();
for (Element userElement : usersSubElementList) {
//String attributeValue(String name);获取指定属性名称的属性值
System.out.println("users标签的子标签"+userElement.getName());
System.out.println("users标签的子标签的id属性值是"+userElement.attributeValue("id"));
System.out.println("users标签的子标签的country属性值"+userElement.attributeValue("country"));
System.out.println("users标签的子标签的sources属性值"+userElement.attributeValue("source"));
System.out.println("3、获取user的子标签列表");
List<Element> userSubElementList = userElement.elements();
for (Element userSubElement : userSubElementList) {
System.out.println("user标签下的子标签名字是"+userSubElement.getName());
//String getText();获取标签的文本
System.out.println("user标签下的子标签的文本是"+userSubElement.getText());
}
}
//获取users标签的第一个user标签
Element firstUserElement = rootElement.element("user");
//获取第一个user标签下的子标签password属性的文本
//String elementText(String name);获取指定名称的子标签的文本
String password = firstUserElement.elementText("password");
System.out.println("第一个user标签的子标签password的文本"+password);
} catch (DocumentException e) {
e.printStackTrace();
}
}
}
输出结果:
HTTP协议有它自己的一些特点,分别是:
基于TCP协议: 面向连接,安全
TCP是一种面向连接的(建立连接之前是需要经过三次握手)、可靠的、基于字节流的传输层通信协议,在数据传输方面更安全。
基于请求-响应模型的: 一次请求对应一次响应
HTTP协议是无状态协议:**每次请求-响应都是独立的,**对于事物处理没有记忆能力。
无状态指的是客户端发送HTTP请求给服务端之后,服务端根据请求响应数据,响应完后,不会记录任何信息。这种特性有优点也有缺点,
请求之间无法共享数据会引发的问题,如:
加入购物车
和去购物车结算
是两次请求,**会话技术**可以解决这个问题
请求行: HTTP请求中的第一行数据,请求行包含三块内容,分别是 GET[请求方式] /[请求URL路径] HTTP/1.1[HTTP协议及版本]
请求方式有七种,最常用的是GET和POST
请求头: 第二行开始,格式为key: value形式
请求头中会包含若干个属性,常见的HTTP请求头有:
Host: 表示请求的主机名
User-Agent: 浏览器版本,例如Chrome浏览器的标识类似Mozilla/5.0 ...Chrome/79,IE浏览器的标识类似Mozilla/5.0 (Windows NT ...)like Gecko;
Accept: 表示浏览器能接收的资源类型,如text/*,image/*或者*/*表示所有;
Accept-Language: 表示浏览器偏好的语言,服务器可以据此返回不同语言的网页;
Accept-Encoding: 表示浏览器可以支持的压缩类型,例如gzip, deflate等。
**数据用处:**浏览器兼容问题
请求体: POST请求的最后一部分,存储请求参数
如上图红线框的内容就是请求体的内容,请求体和请求头之间是有一个空行隔开。此时浏览器发送的是POST请求,为什么不能使用GET呢?这时就需要回顾GET和POST两个请求之间的区别了:
响应行:响应数据的第一行,响应行包含三块内容,分别是 HTTP/1.1[HTTP协议及版本] 200[响应状态码] ok[响应状态码的描述]
关于响应状态码(主要认识三个):
响应头:第二行开始,格式为key:value形式
响应头中会包含若干个属性,常见的HTTP响应头有:
Content-Type:表示该响应内容的类型,例如text/html,image/jpeg;
Content-Length:表示该响应内容的长度(字节数);
Content-Encoding:表示该响应压缩算法,例如gzip;
Cache-Control:指示客户端应如何缓存,例如max-age=300表示可以最多缓存300秒
响应体: 最后一部分。存放响应数据
上图中…这部分内容就是响应体,它和响应头之间有一个空行隔开。
自定义一个服务器需要具备哪些基本的功能?
监听端口: 使用 ServerSocket
监听指定端口,等待客户端的连接请求。
ServerSocket ss = new ServerSocket(8080);
接受连接: 使用 accept()
方法接受客户端的连接请求,创建一个与客户端通信的 Socket
。
Socket sock = ss.accept();
多线程处理连接: 为每个客户端连接创建一个独立的线程,以允许服务器同时处理多个连接。在这个例子中,使用 Handler
类继承自 Thread
。
Thread t = new Handler(sock);
t.start();
处理请求: 在 Handler
类的 run
方法中,通过输入流 (InputStream
) 读取客户端发来的HTTP请求,根据请求内容生成相应的响应,然后通过输出流 (OutputStream
) 发送响应给客户端。
包含**读取请求行,解析请求行,读取请求头部**等等;
private void handle(InputStream input, OutputStream output) throws IOException {
// 处理HTTP请求
// 1.读取请求行
// 2.解析请求行
// 3.读取请求头部
// 4.....
// 发送HTTP响应
// 1.发送HTTP响应头
// 2.读取并发送响应体
// 3.....
}
关闭连接: 在处理完成后,确保关闭与客户端的连接,释放资源。
this.sock.close();
错误处理: 处理异常情况,例如客户端断开连接或发生其他错误。
} catch (Exception e) {
// 处理异常并关闭套接字
System.out.println("客户端断开连接。");
}
发送HTTP响应: 根据请求的内容,发送适当的HTTP响应,包括**状态码、响应头和响应体**。
writer.write("HTTP/1.1 200 OK\r\n");
writer.write("Connection: keep-alive\r\n");
writer.write("Content-Type: text/html\r\n");
writer.write("Content-Length: " + length + "\r\n");
writer.write("\r\n"); // 空行分隔头部和正文
writer.write(data.toString());
writer.flush();
读取文件内容: 从文件中读取内容,用于构建HTTP响应体。
BufferedReader br = new BufferedReader(new FileReader("http/html/a.html"));
StringBuilder data = new StringBuilder();
// 读取文件内容并构建响应体
// ...
br.close();
这是一个简单的HTTP服务器的基本结构,实际的服务器可能需要更多功能,例如安全性、性能优化、支持更多HTTP方法和状态码、动态内容生成等。
Tomcat就是一种servletTomcat 是一个 Web 服务器,同时也是一个 Servlet 容器
Servlet是JavaWeb最为核心的内容,它是Java提供的一门动态web资源开发技术。
Servlet是JavaEE规范之一,其实就是一个接口,将来我们需要定义Servlet类实现Servlet接口,并由web服务器运行Servlet
加载和实例化:默认情况下,当Servlet第一次被访问时,由容器创建Servlet对象
默认情况,Servlet会在第一次访问被容器创建,但是如果创建Servlet比较耗时的话,那么第一个访问的人等待的时间就比较长,用户的体验就比较差,那么我们能不能把Servlet的创建放到服务器启动的时候来创建,具体如何来配置?
@WebServlet(urlPatterns = "/demo1",loadOnStartup = 1)
loadOnstartup的取值有两类情况
负整数:第一次访问时创建Servlet对象
0或正整数:服务器启动时创建Servlet对象,数字越小优先级越高
初始化:在Servlet实例化之后,容器将调用Servlet的**init()方法初始化这个对象,完成一些如加载配置文件、创建连接等初始化的工作。该方法只调用一次**
void init(ServletConfig config)
请求处理:每次请求Servlet时,Servlet容器都会调用Servlet的**service()**方法对请求进行处理
void service(ServletRequest req, ServletResponse res)
服务终止:当需要释放内存或者容器关闭时,容器就会调用Servlet实例的**destroy()**方法完成资源的释放。在destroy()方法调用之后,容器会释放这个Servlet实例,该实例随后会被Java的垃圾收集器所回收
void destroy()
init方法在Servlet对象被创建的时候执行,只执行1次
service方法在Servlet被访问的时候调用,每访问1次就调用1次
destroy方法在Servlet对象被销毁的时候调用,只执行1次
getServletInfo
:该方法用来返回Servlet的相关信息,没有什么太大的用处,一般我们返回一个空字符串即可
String getServletInfo()
public String getServletInfo() {
return "";
}
getServletConfig
:获取ServletConfig对象;服务器在创建Servlet对象的时候会调用init方法,必定会传入一个ServletConfig对象,我们只需要将服务器传过来的ServletConfig进行返回即可
public void init(ServletConfig config) throws ServletException {
this.servletConfig = config;
System.out.println("init...");
}
public ServletConfig getServletConfig() {
return servletConfig;
}
环境搭建:
mvc_project
,导入Servlet依赖坐标<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
package com.baidu.web;
import javax.servlet.*;
import java.io.IOException;
public class ServletDemo1 implements Servlet {
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("this service is running.........");
}
public void init(ServletConfig servletConfig) throws ServletException {
}
public ServletConfig getServletConfig() {
return null;
}
public String getServletInfo() {
return null;
}
public void destroy() {
}
}
@WebServlet("/demo1")
http://localhost:8081/mvc_project_war/demo1
执行流程图:
http://localhost:8080/web-demo/demo1
请求,从请求中可以解析出三部分内容,分别是localhost:8080
、web-demo
、demo1
localhost:8080
可以找到要访问的Tomcat Web**服务器**web-demo
可以找到部署在Tomcat服务器上的web-demo**项目**demo1
可以找到要访问的是项目中的哪个**Servlet类**,根据@WebServlet后面的值进行匹配Request是请求对象,Response是响应对象
ServletRequest和HttpServletRequest是继承关系,并且两个都是接口,接口是无法创建对象的;
传入的对象都是RequestFacade
HTTP请求数据总共分为三部分内容,分别是请求行、请求头、请求体。
请求行包含三块内容,分别是请求方式
、请求资源路径
、HTTP协议及版本
String getMethod()
:获取请求方式: GET
String getContextPath()
:获取虚拟目录(项目访问路径): /request-demo
StringBuffer getRequestURL()
:获取URL(统一资源定位符): http://localhost:8080/request-demo/req1
String getRequestURI()
:获取URI(统一资源标识符): /request-demo/req1
String getQueryString()
:获取请求参数(GET方式): username=zhangsan&password=123
/**
* request 获取请求数据
*/
@WebServlet("/demo2")
public class RequestDemo1 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String getMethod():获取请求方式: GET
String method = req.getMethod();
System.out.println(method);//GET
// String getContextPath():获取虚拟目录(项目访问路径):/mvc_project_war
String contextPath = req.getContextPath();
System.out.println(contextPath);
// StringBuffer getRequestURL(): 获取URL(统一资源定位符):http://localhost:8081/mvc_project_war/demo2
StringBuffer url = req.getRequestURL();
System.out.println(url.toString());
// String getRequestURI():获取URI(统一资源标识符): /mvc_project_war/demo2
String uri = req.getRequestURI();
System.out.println(uri);
// String getQueryString():获取请求参数(GET方式): username=zhangsan&passwrod=123
String queryString = req.getQueryString();
System.out.println(queryString);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
}
}
请求路径:
http://localhost:8081/mvc_project_war/demo2?username=zhangsan&passwrod=123
tips:
如果添加了相关Servlet之后,启动服务器却显示404,没找到相关资源,可尝试将当前项目从服务器的部署中删除,然后再重新部署进去
先删除,再添加部署
String getHeader(String name)
:根据请求头名称获取对应值
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取请求头: user-agent: 浏览器的版本信息
String agent = req.getHeader("user-agent");
System.out.println(agent);
}
打印信息:
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
浏览器在发送GET请求的时候是没有请求体的,POST才会有请求体
Request有两种方式来获取请求体中的数据
ServletInputStream getInputStream()
该方法可以获取字节
BufferedReader getReader()
用html的表单测试:
前端:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<form action="/request-demo/req1" method="post">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit">
form>
body>
html>
后端:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取表单数据
String username = req.getParameter("username");
String password = req.getParameter("password");
// 打印数据
System.out.println("Username: " + username);
System.out.println("Password: " + password);
}
结果:
后端:
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取post 请求体:请求参数
//1. 获取字符输入流
BufferedReader br = req.getReader();
//2. 读取数据
String line = br.readLine();
System.out.println(line);
}
结果:
用postman发送一个POST请求,并携带相关数据(为什么会失败?)
创建新的workspace:
创建成功:
选择刚才所创建的workspace:
可以新建一个文件夹,用于测试的分类
右键,点击添加请求
测试结果为什么是:
为什么?
可以使用IDEA提供的模板来制作一个Servlet的模板
原因:
POST:TOMCAT在获取流的时候采用的编码是ISO-8859-1;ISO-8859-1编码是不支持中文的,所以会出现乱码
解决办法:把TOMCAT在获取流数据之前的编码设置为UTF-8
//1. 解决乱码: POST getReader()
//设置字符输入流的编码,设置的字符集要和页面保持一致
request.setCharacterEncoding("UTF-8");
//2. 获取username
String username = request.getParameter("username");
System.out.println(username);
GET:如图
由于前后编码与解码采用的格式不一样,就会导致后台获取到的数据为乱码。
解决办法:
1.按照ISO-8859-1编码获取乱码字符对应的字节数组
2.按照UTF-8编码获取字节数组对应的字符串
3.API
编码:
java.net.URLEncoder.encode("需要被编码的内容","字符集(UTF-8)")
解码:
java.net.URLDecoder.decode("需要被解码的内容","字符集(UTF-8)")
String username = request.getParameter("username");
System.out.println("解决乱码前:"+username);
username = new String(username.getBytes(StandardCharsets.ISO_8859_1),StandardCharsets.UTF_8);
System.out.println("解决乱码后:"+username);
请求转发(forward): 一种在服务器内部的资源跳转方式
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A处理完请求后将请求发给资源B
(3)资源B处理完后将结果响应给浏览器
(4)请求从资源A到资源B的过程就叫请求转发
(5)请求转发的实现方式:
req.getRequestDispatcher("资源B路径").forward(req,resp);
示例:
/**
* 请求转发
*/
@WebServlet("/req5")
public class RequestDemo5 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("demo5...");
//请求转发
request.getRequestDispatcher("/req6").forward(request,response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doGet(request, response);
}
}
请求转发资源间共享数据: 使用Request对象
三个方法:
void setAttribute(String name,Object o);
存储数据到request域[范围,数据是存储在request对象]中
Object getAttribute(String name);
根据key获取值
void removeAttribute(String name);
根据key删除该键值对
对比:
Reponse的继承体系和Request的继承体系也非常相似:
HTTP响应数据总共分为三部分内容,分别是响应行、响应头、响应体
void setStatus(int sc)
:设置响应状态码
void setHeader(String name,String value)
:设置响应头键值对
对于响应体,是通过字符、字节输出流的方式往浏览器写
PrintWriter getWriter();
获取字符输出流:
ServletOutputStream getOutputStream();
获取字节输出流
Response重定向(redirect): 一种资源跳转方式
(1)浏览器发送请求给服务器,服务器中对应的资源A接收到请求
(2)资源A现在无法处理该请求,就**会给浏览器响应一个302的状态码+location的一个访问资源B的路径**
(3)浏览器接收到响应状态码为302就会重新发送请求到location对应的访问地址去访问资源B
(4**)资源B接收到请求后进行处理并最终给浏览器响应结果**,这整个过程就叫重定向
//重定向
//1.设置响应状态码 302
response.setStatus(302);
//2. 设置响应头 Location
response.setHeader("Location","/request-demo/resp2");
或者简化代码:
resposne.sendRedirect("/request-demo/resp2")
要想将字符数据写回到浏览器
通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
通过字符输出流写数据: writer.write(“aaa”);
可以写html文件,但要告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签
response.setHeader("content-type","text/html");
writer.write("aaa
");
返回中文的字符串,需要注意设置响应数据的编码为utf-8
//设置响应的数据格式及数据的编码
response.setContentType("text/html;charset=utf-8");
writer.write("你好");
要想将字节数据(例如在写图片时可能会用到)写回到浏览器,我们需要两个步骤:
通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();
通过字节输出流写数据: outputStream.write(字节数据);
示例:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 读取文件
FileInputStream fis = new FileInputStream("d://a.jpg");
//2. 获取response字节输出流
ServletOutputStream os = response.getOutputStream();
//3. 完成流的copy
byte[] buff = new byte[1024];
int len = 0;
while ((len = fis.read(buff))!= -1){
os.write(buff,0,len);
}
fis.close();
}
添加虚拟目录**
要想将字符数据写回到浏览器
通过Response对象获取字符输出流: PrintWriter writer = resp.getWriter();
通过字符输出流写数据: writer.write(“aaa”);
可以写html文件,但要告诉浏览器返回的数据类型是HTML类型数据,这样浏览器才会解析HTML标签
response.setHeader("content-type","text/html");
writer.write("aaa
");
返回中文的字符串,需要注意设置响应数据的编码为utf-8
//设置响应的数据格式及数据的编码
response.setContentType("text/html;charset=utf-8");
writer.write("你好");
要想将字节数据(例如在写图片时可能会用到)写回到浏览器,我们需要两个步骤:
通过Response对象获取字节输出流:ServletOutputStream outputStream = resp.getOutputStream();
通过字节输出流写数据: outputStream.write(字节数据);
示例:
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1. 读取文件
FileInputStream fis = new FileInputStream("d://a.jpg");
//2. 获取response字节输出流
ServletOutputStream os = response.getOutputStream();
//3. 完成流的copy
byte[] buff = new byte[1024];
int len = 0;
while ((len = fis.read(buff))!= -1){
os.write(buff,0,len);
}
fis.close();
}