《Java Web 基础与实例教程》笔记1

对自己说:

        前面主要讲了java web 基础知识以及Servlet接受GET请求数据,码了3W字,基本抄书还不不敢公开,从书上第三章开始,尽可能根据自己的理解来记笔记。
有实例源码,人麻了


目录

第三章 Servlet 接受 POST 请求数据

FORM表单

1. 表单标签

2.表单控件

3.表单按钮

小露身手:利用 FORM 表单模拟发送 GET 请求数据

URL 路径定位方法

URL绝对路径

URL相对路径

page-relative 路径的技巧

小露身手:URL 路径定位方法

过程分析:浏览器发送 POST 请求数据和 Web 服务器接收 POST 请求数据

准备工作

过程分析:浏览器向 Web 服务器发送 POST 请求数据

POST请求数据的构成

小露身手:request 请求对象接收 application/x-www-form-urlencoded 格式的 POST 请求数据

小露身手:向 Servlet 添加 multipart/form-data 支持

实践任务 Servlet 接收 POST 请求数据

 

第四章 Servlet 生成 HTTP 响应数据

HTTP响应数据与HTTPServletResponse之间的关系

HTTP 响应行

        响应状态码

        使用 response 响应对象设置响应状态码 

HTTP响应头列表

        常见的响应头

        使用 response 响应对象设置、添加响应头

重定向、定时刷新和请求转发的比较

1.重定向和定时刷新的区别

2.请求转发与重定向的区别

使用response响应对象生成HTTP响应体

response响应对象的缓存

向response缓存添加文本型数据

向response缓存添加字节型数据

response响应对象一石三鸟的代码

实践任务        Servlet生成HTTP响应数据

场景1步骤:

场景2步骤 使用response响应对象设置状态响应码

场景3 使用response响应对象设置响应头列表

场景4步骤 重定向、定时刷新、请求转发、请求包含等的使用以及文本型数据封装到响应体

场景5使用Servlet的response响应对象,在浏览器页面中显示图片或者下载图片

 场景7 向Servlet程序的doGet()方法新增如下代码

 



第三章 Servlet 接受 POST 请求数据

FORM表单

Form 表单由表单标签、表单控件和表单按钮三部分组成。

1. 表单标签

外观上:类似于 Excel 工作表的虚框。表单标签像编剧,默默存在,决定剧情

功能上:定义了数据的发送方式、处理数据、数据的 MIME。表单标签的语法格式如下:

<form method="post" action="处理程序" enctype="multipart/form-data" >
这里是表单控件的代码和表单按钮的代码
</form>

重点属性讲解如下:

method:设置 FORM 表单数据的发送方式,值为 get 或 post,默认为GET

action:设置FORM表单里输入的数据发送给哪个程序处理。若不设置,或者值为空字符串(即 action="")时,表示表单数据发送给自己(当前程序)处理。action 设置出错,将会导致 404 错误。

enctype:设置表单数据的内容格式(实际上是MIME)。若不设置,默认值为 application/x-www-form-urlencoded。如果希望通过表单上传文件,enctype 必须设置为 multipart/form-data,并且 method 必须设置为 post。

2.表单控件

外观上:表单控件在浏览器上可见(隐藏域除外)。表单控件像演员

功能上:允许浏览器用户输入数据或者选择数据。表单控件包括单行文本框、密码框、隐藏域、复选框、单选按钮、文件上传框、多行文本框和下拉选择框等

单行文本框:

用户名: <input type="text" name="name" value="victor" id="ID值"/>

显示效果为:

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_8,color_FFFFFF,t_70,g_se,x_16

 属性讲解:

type="text":定义单行文本输入框

name:定义表单控件的名字,几乎所有的表单控件都有名字,Servlet 程序通过 name 的值区分各个表单控件。

value:定义初始值

id:设置了唯一标识符,唯一标记 HTML 页面上的元素。在同一个 HTML 页面上,必须确保ID值唯一,不能重复

说明:如果没有设置 type 属性,那么 type 属性默认值为 text。

密码框:

密码:<input type="password" name="password" value="1234"/>

显示效果为:

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_7,color_FFFFFF,t_70,g_se,x_16

属性讲解:

type="password":定义密码框

隐藏域: 

<input type="hidden" name="userID" value="6"/>

隐藏域,顾名思义,隐藏域在浏览器上不可见

属性讲解:同上,举一反三

复选框:

<input name="inserest" type="checkbox" value="music"/>音乐
<input name="inserest" type="checkbox" value="game" checked/>游戏
<input name="inserest" type="checkbox" value="film" checked/>电影

显示效果:

3b4bd2bebdc74d68b3bc3fe990d21a15.png

重点讲解:

type="checkbox":定义复选框

checked:表示该复选框默认被选中,该属性无须设置值 

单选按钮:

<input name="sex" type="radio" value="male" checked/>男
<input name="sex" type="radio" value="female"/>女

显示效果:

8cef5e233a2043d9877dbf973bd2ab6f.png

重点讲解:

name:定义表单控件的名字。要想保持单选按钮之间互相 “排斥”,必须保证单选按钮的   name相同。

type="radio":定义单选按钮

checked:参考复选框的 checked

文件上传框:

<input type="file" name="myFiles" multiple/>

显示效果,注:点击选择文件弹出的框在网页界面的左上角: 

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 重点属性讲解:

type="file":定义了文件上传框

multiple:表示允许选中多个文件(按住 Ctrl 键来选中多个文件)。若无,表示只能选中一个文件。

注意:表单标签 <form>的 enctype 属性必须设置为 multipart/form-data,method 属性必须设置为 post,才能上传文件。

多行文本框:

备注:<textarea name="remark" cols="30" row="4">示例代码</textarea>

显示效果:

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_9,color_FFFFFF,t_70,g_se,x_16

重点属性讲解:

cols:定义多行文本框的宽度(单位是px)

rows:定义多行文本框的高度(单位是px)

content:多行文本框默认显示的文字内容(这里是"示例代码")

下拉选择框:

下拉选择框分为单选下拉选择框(类似于单选按钮)和多选下拉框(类似复选框)

<select name="hobby" size="3" multiple>
    <option value="music" selected>音乐</option>
    <option value="game" selected>游戏</option>
    <option value="film">电影</option>
</select>

 显示效果:

69b86d8079bb45509968a95da61077e1.png

<select>标签用于定义下拉选择框,重点属性讲解如下:

size:定义下拉选择框的高度,默认值为1

multiple属性:同上文件选取

<option>标签用于定义下拉选择框,重点属性讲解如下:

value:指定某个选项的值。若没有指定,则选项的值为<option>和</option>之间的内容。

3.表单按钮

单击按钮触发执行 action 属性指定的处理程序。常用的表单按钮有提交按钮(submit)和重置按钮(reset)

提交按钮:

提交按钮用于将FORM表单输入的数据发送到<form>标签 action 属性指定的处理程序。有两种方法可以创建提交按钮。

<input type="submit" name="login" value="普通提交按钮"/>
<button type="submit" name="login1">普通提交按钮</button>

显示效果为:

7e48fd7ef02b475cb887c4258bc61196.png

值得说明的是:第二行的提交按钮上可以显示一些特殊字符

重置按钮:
重置按钮并不是将表单控件输入的信息清空,而是将表单控件恢复到初始值状态(或者默认状态),初始值由表单控件的 value 值决定。下面两种方式都可以创建重置按钮:

<input type="reset" name="cancel" value="重新输入"/>
<button type="reset" name="login">重新输入</button>

显示效果: 

 9d950a90d90049d8be04834d339ef926.png

小露身手:利用 FORM 表单模拟发送 GET 请求数据

准备工作:

        在 Eclipse 中创建 Dynamic Web Project 项目 post

        将 Servlet-api.jar 包导入 Web 项目

步骤:

(1)在 WebContent 目录下创建 form0.jsp 程序,输入如下代码:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form action="ABCServlet" method="get">
用户名:<input type="text" name="name" value="张三" /><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="shopping" checked>购物
<input type="checkbox" name="hobby" value="music" checked>音乐<br/>
<input type="submit" value="发送"><input type="reset" value="重置">
</form>
</body>
</html>

(2)将第二章 get 项目的 ABCServlet 程序和 BCDServlet 程序复制到 post 项目中

具体步骤:打开 Project Explore 视图 -> 展开刚刚创建的WEB项目 -> 展开 Java Resources -> 右击 src -> 弹出 New Java Package 窗口。Java package  文本框处输入 controller

然后将 get 项目的 ABCServlet 和 BCDServlet 程序复制到 post 的 controller 包中

(3)部署本项目,启动 Tomcat。

(4)打开浏览器,输入网址 http://localhost:8080/post/form0.jsp,执行 form0.jsp 程序,执行结果如图所示:

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_8,color_FFFFFF,t_70,g_se,x_16

(5)单击发送按钮,触发 ABCServlet 的 doGet() 方法执行

分析:form0.jsp 利用 FORM 表单向 ABCServlet 发送了 GET 请求,并且发送的数据与第二章直接在浏览器地址栏上输入的URL发送的数据完全相同。因此,本次 Tomcat 控制台输出的信息几乎和第二章的完全相同(部分请求头信息不一样,例如请求中新增了referer请求头)

URL 路径定位方法

前引:ABCServlet.java 的 urlPatterns 是 “/ABCServlet”,能不能将 form0.jsp 里 <form> 标签里的 action 属性值 “ABCServlet” 修改 “/ABCServlet” 呢?答案明显是不能的,否则会出现404错误,如何设置正确的路径,让一个程序能够访问另一个资源呢?

URL路径可以分为绝对路径和相对路径,而相对路径可以再分为:server-relative 路径和page-relative 路径。

URL绝对路径

URL绝对路径通常在访问系统外部资源时才使用,访问系统内部资源一般使用URL相对路径。

URL相对路径

使用URL相对路径访问某个 WEB 服务器的目的资源时,Web开发人员必须用清楚两个问题:起始目录在哪儿;目的资源在哪儿(目的路径)。

1.server-relative 路径

该路径的典型特征是以 “/” 开头。“/”的含义又可以细分为如下两种情形

情形1:A 通过浏览器访问目的资源 B

        典型的有:通过表单<form>标签里的action属性、超链接<a>标签里的 href 属性、CSS样式表<link>标签里的 href 的属性、JavaScript<script>标签里的 src 属性、图片<img>标签的 src 属性、重定向 redirect、定时刷新 refresh,甚至直接在浏览器地址栏中输入目的资源的URL。

        该情形中的 “/” :表示将 Web 服务器的根目录作为初始目录。例如本地 Web 服务器根目录对应的 URL 是 http://localhost:8080。

例如:form0.jsp中的action属性属于情形该情形,server-relative 路径应该是 /post/ABCServlet(ABCServlet 相对于 Web 服务器根目录的目录层次是 /post/ABCServlet)

回顾:获取Web项目虚拟路径的方法是 request.getContextPath()

情形2:Web 项目的程序 A 直接访问该项目的目的资源 B

典型的有请求转发和请求包含

该情形的共同特征是:A 访问 B 是在 Web 项目内完成,与浏览器没有任何关系。

该情形的 “/”:表示将 Web 项目的虚拟路径作为起始目录(Web 项目的虚拟路径对应 Web 项目根目录,注意不是 Web 服务器的根目录)

例如:ABCServlet 将 request 请求对象请求转发至目的资源 BCDServlet ,此时server-relative路径为:/BCDServlet(起始目录:http://localhost:8080/post 目的路径:http://localhost:8080/post/BCDServlet;计算路径将两者相减得出BCDServlet相对于Web项目虚拟路径的目录层次即server-relative)

将操场当做 Web 服务器,操场的每条跑道看做 Web 服务器上部署的 Web 项目;跑道上的每名运动员看做 Servlet 程序,接力棒看做 request 请求对象,场外工作人员看做浏览器,操场上的每条跑道可以同时进行接力比赛,就像 Web 服务器可以同时部署多个 Web 项目;接力棒只能在同一个跑道内传递和接收,就像 request 请求对象只能在同一个 Web 项目内派发。

2. page-relative 路径

page-relative 路径将当前程序所在的 URL 路径作为起始目录,page-relative 路径的典型特征是不以 “/” 开头

        示例1:form0.jsp 中的 action 属性怎么输入 page-relative 路径呢?

        起始目录:form0.jsp 对应的 URL 路径是 http://localhost:8080/post/form0.jsp,因此 form0.jsp 文件的起始目录是 http://localhost:8080/post/

        目的路径:form0.jsp 中的 action 属性要访问的目的资源是 ABCServlet,而 ABCServlet 对应的 URL 路径是:http://localhost:8080/post/ABCServlet

        计算路径:http://localhost:8080/post/ABCServlet - http://localhost:8080/post/

        计算结果:ABCServlet 相对于 form0.jsp 的目录层次是 ABCServlet.

位于同一目录,使用 page-relative 路径时,它们之间可以通过名称直接访问

page-relative 路径的技巧

  1. 使用目录分隔符时,尽量使用 “ / ” (而不是 “ \ ”),这样更由于程序需在不同操作系统(Windows 和 Linux 等)间的移植
  2. 可以使用 “ . ” 表示当前目录
  3. 位于同一目录的两个程序,“直呼其名”即可访问
    例如 http://localhost:8080/1/2/a.jsp(如果 a.jsp 在2楼)访问 http://localhost:8080/1/2/b.jsp。因此 page-relative 路径是 “b.jsp”,也可以写作 “./b.jsp”
  4.  A 访问下级目录中的资源 B,直接指定下级目录层次和资源 B 的文件名即可。
    例如 http://localhost:8080/1/a.jsp 访问 http://localhost:8080/1/2/3/b.jsp。因此 a.jsp 访问 b.jsp 的 page-relative 路径是 “ 2/3/b.jsp ”,也可以写成 “./2/3/b.jsp”
  5. 可以使用 “../”表示当前目录的上级目录
  6.  A 访问上级目录中的资源 B,需要使用“ ../ ”

小露身手:URL 路径定位方法

场景1 使用绝对路径,访问目的资源

步骤:

 (1)form1.jsp文件创建 <body></body>中添加如下代码:

<%
String contextPath = request.getContextPath();
String basePath = request.getScheme() + "://" + request.getHeader("host") + contextPath;
%>
<form action="<%=basePath %>/ABCServlet" method="get" >
用户名:<input type="text" name="name" value="张三" /><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="shopping" checked>购物
<input type="checkbox" name="hobby" value="music" checked>音乐<br/>
<input type="submit" value="发送"><input type="reset" value="重置">
</form>

说明1:form1.jsp 的目的资源时 ABCServlet ,而 ABCServlet 的 URL 绝对路径是 http://localhost:8080/post/ABCSevlet,格式如下:

        请求协议://接收请求的主机域名或者IP地址 + :+端口号/虚拟路径/ABCServlet

说明2:Java 变量 basePath 的值是一个封装了 ABCServlet 的URL绝对路径的字符串

说明3:代码片段“<%=basePath %>” 的功能是,将 Java 变量 basePath 的值输出到当前页面。

运行,检测URL路径是否正确。

场景2 通过浏览器,使用 server-ralative 相对路径访问目的资源

步骤:

(1)form2.jsp文件创建 <body></body>中添加如下代码:

<%
String contextPath = request.getContextPath();
%>
<form action="<%=contextPath %>/ABCServlet" method="get" >
用户名:<input type="text" name="name" value="张三" /><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="shopping" checked>购物
<input type="checkbox" name="hobby" value="music" checked>音乐<br/>
<input type="submit" value="发送"><input type="reset" value="重置">
</form>

说明:form2.jsp 的目的资源文件是 ABCServlet。server-relative 相对路径的起始目录是 Tomcat 根目录,对应的 URL 是 http://localhost:8080,目的资源 ABCServlet 所对应的 URL 路径是 http://localhost:8080/post/ABCServlet。因此,form2.jsp 使用路径 “/post/ABCServlet”,即可访问 ABCServlet。

结论:通过浏览器使用 server-relative 相对路径访问目的资源时,需要指定 Web 项目的虚拟路径 contextPath。

场景3 使用 page-relative 路径,访问目的资源

步骤:

        (1)form3.jsp 创建,并将代码片段 action = "./ABCServlet"
说明:千万不要忽视 “ / ”前面的 “ . ”,访问的路径就变成 server-relative 路径,变成了从Tomcat 服务器根目录访问 ABCServlet。

场景4 浏览器端访问目的资源的方法比较

步骤:
       
(1)如图,并在 Folder Name 处输入 test
watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

        (2)将form0~3.jsp,复制到 test目录,并依次运行这4个jsp程序。
        (3)单击发送按钮,测试URL路径是否正确

结论:A 使用 page-relative 路径访问目的资源 B 时,如果A的当前目录发生了变化,目录层次也要跟着发生变化。考虑到项目移植,不建议使用 page-relative 路径访问目的资源。使用URL绝对路径 和 server-relative 路径,不用在意自己所处的位置。另外由于server-relative 路径简单明了,推荐使用 server-realative 路径访问目的资源B,综上所述,推荐使用场景2 中的URL路径定位方法。

过程分析:浏览器发送 POST 请求数据和 Web 服务器接收 POST 请求数据

准备工作

若要让浏览器发出 POST 请求,需要借助 FORM 表单,准备工作如下。

  1. 复制 form2.jsp,让新文件命名为 form4.jsp
  2. 将 form4.jsp 中的代码片段 method="get"修改为 method="post"
  3. 在浏览器输入 http://localhost:8080/post/form4.jsp

     ps:这是浏览器向Web服务器发送的第一次 http 请求,该请求是GET请求。

过程分析:浏览器向 Web 服务器发送 POST 请求数据

  1. 浏览器显示了 form4.jsp的Form表单
  2. 单击发送,浏览器自动将Form表单数据“封装”成POST请求数据(这因为FORM表单的method 设置为 post)
  3. 浏览器主机与Web服务器建立网络连接(TCP/IP连接)
  4. 浏览器将“封装”好的POST请求数据发送到Web服务器。浏览器等待 Web 服务器响应。

POST请求数据的构成

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

常见的POST请求头及其含义如下:

        Content-Length:浏览器通知 Web 服务器,POST请求体长度(单位是字节)。POST请求头必有Content-length。如果POST请求数据中没有请求体,那么Content-Length的值为0。

        注意:POST请求体长度与字符编码有直接关系,如果没有指定字符集,大部分浏览器默认使用UTF-8编码。

        Content-Length用于标记POST请求体的结束。
        Cache-Control:max-age=0,表示若请求头中包含此内容,浏览器使用浏览器的缓存资源之前,先进行缓存资源是否有效的验证。

        说明1:输入网址,按Enter键,浏览器不一定会向 Web 服务器发送请求。有事为了环节压力,Web服务器可以控制浏览器,将浏览器上次访问的静态资源(例如 JavaScript文件、CSS文件、图片文件)缓存到浏览器本地磁盘。浏览器再次发出请求时,先自行判断这些本地资源是否在有效期内,若在有效期内,直接从本地获取即可,没必要从Web服务器下载这些静态资源。

        请求2:在请求头和响应头中,都可以定义Cache-Control,但意义不同。

        请求头中的 Cache-Control,浏览器发出HTTP请求时,浏览器判断它是否使用本地缓存资源。
        响应头中的 Cache-Control,Web服务器用它控制浏览器,让浏览器设置缓存资源的有效期。

        Origin:浏览器通知 Web 服务器,本次HTTP请求从哪里发起,其值仅包括协议、域名、端口号。

        Content-Type:浏览器通知 Web 服务器,POST 请求体的内容格式(实际上是MIME),要么是 application/x-www-form-urlencoded,要么是 multipart/form-data,取决于<form>标签 enctype 的值。

        说明:Content-Length 请求头和 Content-Type 请求头是POST请求数据所特有的,GET请求数据没有这两个请求头。其他请求头,例如 Referer、Origin、Cache-Control以及Cookie,并不是POST请求数据所特有的,GET请求数据也可以有这些请求头。

        Referer:引用页或者推荐人。浏览器通知Web服务器,本次HTTP请求来自哪个URL。Referer主要用于统计访问本网站

        Cooike:浏览器通知 Web 服务器,本次HTTP请求的Cooike信息,有关Cooike和Session的更多知识在第六章。

        空行:POST 请求数据的空行和GET请求数据的空行功能相同

        POST请求体:两种内容格式 application/x-www-form-urlencoded 和 multipart/form-data,由<form>标签的 enctype 属性值决定。 

GET和POST对比:

  1. 请求行:POST请求类型是POST,GET是GET
  2. POST请求特有
  3. 请求参数:GET请求的请求参数位于请求行,通过查询字符串发送到 Web 服务器,查询字符串被浏览器进行 URL 编码。POST请求的请求参数位于POST请求体
  4. 其他对比:GET没有请求体,POST可以没有请求体

小露身手:request 请求对象接收 application/x-www-form-urlencoded 格式的 POST 请求数据

准备工作

(1)向 ABCServlet 类的 doPost()方法中加 doGet(request,response);

(2)重新运行 form4.jsp,打开表单,单击 “发送” 按钮,POST请求数据由 doGet() 方法到控制台上。

场景1 GET请求与POST请求的区别之乱码问题

        1. 浏览器编码方式和 Tomcat 服务器解码方式相同,因此使用request请求对象获取GET请求数据时,不会出现中文字符乱码问题

        2. POST请求体的字符编码并不是由浏览器决定的,而是由JSP程序的 contentType 的charset 属性值决定的。POST请求体的字符编码无法统一,造成了 Tomcat 无法采用统一的字符解码方案。

解决方案:在 Servlet 程序中手动调用 request.setCharacterEncoding(String charset),将接收到的POST请求体里的参数,按照 charset 值解码

场景2 GET请求与POST请求的区别之请求转发不同

        结论:POST请求的请求转发触发doPost()方法运行;同理doGet();

                   浏览器 向ABCServlet 用了doPost() 方法,调用了本身的 doGet()方法,后请求转发了BCDServlet,则调用的是BCDServlet 的doPost()方法。

场景3 GET请求与POST请求的区别之请求头不同

        前文提及,不加赘述。

场景4 GET请求与POST混合发送的请求数据

准备工作:form2.jsp复制改为form5.jsp改为:

        <form action="<%=contextPath %>/ABCServlet?hobby=gaming&hobby=study" method="post" >

form5调用。

结论1:还是调用 doPost() 方法,表单发送的数据是POST而不是GET

结论2:采用GET请求和POST请求混合发送时,request 请求对象能够接收所有的请求数据

小露身手:向 Servlet 添加 multipart/form-data 支持

form2-->form6,再修改为:

<form action="<%=contextPath %>/ABCServlet" method="Post" enctype="multipart/form-data">

场景1: 向 Tomcat 添加 multipart/form-data 格式支持

<Context docBase="get" path="/get" reloadable="true" source="org.eclipse.jst.jee.server:get"/><Context docBase="post" path="/post" reloadable="true" source="org.eclipse.jst.jee.server:post" allowCasualMultipartParsing="true"/></Host>

watermark,type_ZHJvaWRzYW5zZmFsbGJhY2s,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

场景2:Servlet 添加 multipart/form-data 格式支持

(1)向ABCServlet 添加 multipart/form-data 格式支持

        @WebServlet("/ABCServlet")
        @javax.servlet.annotation.MultipartConfig
        public class ABCServlet extends HttpServlet {

场景3:创建支持多文件上传的FORM表单

步骤:复制form6 --> form7,在<form>加如下代码:

        <input type="file"name="myFiles"    multiple/><br/>

注意1:form的method必须是post,enctype必须设置为multipart/form-data

注意2:句中添加了 multiple,支持多文件选择

实践任务 Servlet 接收 POST 请求数据

1.目的

掌握 FORM 表单发送 GET 和 POST 请求数据的方法

掌握 URL 路径定位的方法

掌握获取 multipart / form - data 内容格式的 POST 请求体参数的方法

掌握多文件上传的实现方法

了解单文件和多文件混合上传的实现方法

2.任务

所有:小露身手

本实践任务:使用 request 请求对象实现单文件和多文件混合上传

说明:本人无的难点在于,如何防止单个文件被上传两次

3.步骤

(1)复制 form7.jsp,将新文件命名为 form8.jsp

(2)将 form8.jsp 修改,以下是修改过的 form8.jsp 代码

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<%
String contextPath = request.getContextPath();
%>
<form action="<%=contextPath %>/ABCServlet" method="Post" enctype="multipart/form-data">
用户名:<input type="text" name="name" value="张三" /><br/>
兴趣爱好:<input type="checkbox" name="hobby" value="shopping" checked>购物
<input type="checkbox" name="hobby" value="music" checked>音乐<br/>
只能选择单个文件:<input type="file" name="myFile"	/><br/>
可以选择多个文件:<input type="file" name="myFiles" multiple	/><br/>
<input type="submit" value="发送"><input type="reset" value="重置">
</form>
</body>
</html>

(3)将 ABCServlet 的 doPost() 方法,修改如下:

	protected void doPost(HttpServletRequest request, HttpServletResponse response) 
			throws ServletException, IOException {
//		doGet(request, response);
		System.out.println("执行ABCServlet的doPost()方法!");
		request.setCharacterEncoding("UTF-8");
		doGet(request,response);
		//我用form4.jsp运行,contentType="text/html; charset=UTF-8"他上面用UTF-8 就只能用UTF-8(兼容)
		
		//场景3 GET请求与POST请求的区别之请求头不同
		System.out.println(request.getContentLength());//没有Post请求体返回0,是GET请求体返回-1
		System.out.println(request.getContentType());
		if("application/x-www-form-urlencoded".equals(request.getContentType())) {
			System.out.println("application/x-www-form-urlencoded格式的数据接收完毕!");
			return;
		}
		
		String uploadDIR = "/upload/";
		String uploadPath = request.getServletContext().getRealPath(uploadDIR);
		javax.servlet.http.Part singlePart = request.getPart("myFile");
		String singleFileName = "";
		//确保单文件上传框中选择了文件
		if(singlePart != null) {
			singleFileName = singlePart.getSubmittedFileName();
			if(singleFileName !=null && !"".equals(singleFileName)) {
				System.out.println("单个上传的文件是:" + singleFileName);
				singlePart.write(uploadPath + singleFileName);
				singlePart.delete();
			}
		}
		java.util.Collection<javax.servlet.http.Part> parts = request.getParts();
		for (javax.servlet.http.Part multipart : parts) {
			String multiFileName = multipart.getSubmittedFileName();
			//通过判断文件名,确保单文件上传框中的文件,不会重复上传
			if (multiFileName !=null && !multiFileName.equals(singleFileName)) {
				System.out.println("多文件上传的文件有:" + multiFileName);
				multipart.write(uploadPath + multiFileName);
				multipart.delete();
			}
		}
		
		System.out.println("ABCServlet的doPost()方法执行完毕!");
	}

 


第四章 Servlet 生成 HTTP 响应数据

HTTP响应数据与HTTPServletResponse之间的关系

        HTTPServletResponse 响应对象的主要功能:在Web服务器端产生HTTP响应数据所需的响应行、响应头列表、响应体。
        Web 服务器返回的响应针对的都是浏览器发向Web服务器的 HTTP 请求。

HTTP 响应行

        响应状态码

        响应状态码:针对当前的HTTP请求,Web服务器通知浏览器处理当前 HTTP 请求的状态码。响应状态码由 3 位数字构成,其中首位数字定义了状态码的类型。响应状态码一共分为5种类型分别是:100+ 、 200+ 、 300+ 、 400+ 、 500+

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

        使用 response 响应对象设置响应状态码 

        void response.setStatus(int):设置响应状态码
        void response.sendError(int,"状态码对应的信息"):设置状态响应码并设置简要描述

        说明:大多数情况下,Web开发人员没有必要记住这些响应状态码,也无须手动设置响应状态码;可以用 int response.getStatus()来获取当前 Servlet 程序的响应状态码的方法

HTTP响应头列表

        常见的响应头

  1. Content-Length:80
    响应体长度,单位是字节
  2. Content-Type:text/html;charset=UTF-8
    响应体的内容格式
  3. Content-Disposition:attachment;filename=aaa.zip
    以文件下载方式下载响应体内容
  4. Transfer-Encoding:chunked
    将响应体分隔成若干数据块,分块传输到浏览器。当使用Response响应对象实现文件下载功能,Web服务器无法确定文件真实长度,无法将Content-Length 响应体写入HTTP响应头列表。Web服务器会自动将HTTP响应头列表中添加一个 Transfer-Encoding 响应头,目前 Transfer-Encoding 响应头的值只能是 chunked(分块编码)。
    值得注意的是,Transfer-Encoding和Content-Length是互斥的,不会出现在同一个HTTP响应头列表
  5. Location:https://www.baidu.com
    重定向到 Location指定的网址
    Location响应头需要和302响应状态码配合使用,才能实现重定向功能
  6. Refresh:10
    每隔10s,刷新一次页面(定时刷新功能)
    提供了类似于重定向的功能示例:Refresh:10;url=http://www.baidu.com(代码通知浏览器,10秒后打开百度页面)
  7. Set-Cookie:cookie=value
    Web服务器通知浏览器,将响应头的 Cookie 信息保存到浏览器主机内存或外存
  8. Connection:keep-alive
    Web服务器和浏览器之间的连接状态
  9. Date:Tue,31 Dec 2019 04:25:57 GMT
    HTTP响应数据的生成时间
  10. Content-Encoding:gzip
    响应体数据的压缩类型
  11. Content-Language:zh-cn
    响应体的语言。zh-ch 表示简体中文

        使用 response 响应对象设置、添加响应头

        response 响应对象提供了两种设置、添加响应头的方法:一种通用方法、一种便捷方法。

  1. 使用通用的方法设置、添加响应头(能够设置、添加浏览器支持的所有响应头)
    void addDateHeader(String name,long date):添加时间格式的响应头,响应头值是长整数。
    void addHeader(String name,String value):添加字符串格式的响应头,响应头值是长整数。
    void addIntHeader(String name,int value):添加整数格式的响应头,响应头值是长整数。
    void setDateHeader(String name,long date):设置时间格式的响应头,响应头值是长整数。
    void setHeader(String name,String value):设置字符串格式的响应头,响应头值是长整数。
    void setIntHeader(String name,int value):设置整数格式的响应头,响应头值是长整数。
    说明1:HTTP响应头中,有些响应头,例如 Content-Length,若存在,也只能存在一个,但有些例如 Set-Cookie,若存在,可以存在多个,set方法设置响应头的时候,若响应头已存在,则会覆盖已有的响应头;若使用add方法,则会据需继续添加,不会覆盖。所以,响应头若能重复,则使用add方法,若不能重复,则使用set方法。
    Content-Length既是有且唯一又值为整数,示例:response.setIntHeader(“Content-Length”,0);
    同理,response.addHeader(“Set-Cookie”,“userName=zhangsan;Path=/;HttpOnly”);
    response.addHeader(“Set-Cookie”,“password=123456;Path=/;HttpOnly”);
    说明2:response提供若干获取当前Servlet程序响应头的方法,列举如下——
            boolean containsHeader(String name):判断 HTTP响应头列表中是否存在响应头名为 name的响应头
            String getHeader(String name):返回响应头名为 name的响应头。若不存在,返回null。
            java.util.Collection<String> getHeaderName
    s():返回HTTP响应头列表中所有的响应头名
            java.util.Collection<String> getHeaders(String name):由于响应头名可以重名,该方法返回响应头名为 name 的所有响应头值
  2. 使用便捷方法设置、添加响应头
    便捷方法只能设置个别常用的响应头
    (1)Web服务器通知浏览器,设置响应体的长度
             response.setContentLength(0);
            相当于response.setIntHeader(“Content-Length”,0);
    (2)Web服务器通知浏览器,设置响应体的MIME和字符集
             response.setContentType(“text / html,charset=UTF-8”);
            相当于response.setContentType(“Content-Type”,“text / html,charset=UTF-8”);
    (3)Web服务器通知浏览器,添加Cookie响应头
              Cookie cookie = new Cookie(“useName”,“zhangsan”);
              cookie.setMaxAge(60*60*24*7);//设置Cooike的生命周期
              response.addCooike(cookie);
             相当于response.addHeader("Set-Cookie","userName=zhangsan;Max-Age=604800;Expires=Sun,29-Sep-2019 14:21:24 GMT");
    (4)Web服务器通知浏览器,设置重定向响应头,代码如下:
            response.sendRedirect("https://www.baidu.com");
            等效于如下通用方法(两行代码):
            response.sendStatus(302);
            response.setHeader("Location","https://www.baidu.com");
    ps:仅仅是第二行代码不会导致重定向的发生,第二行代码产生的响应码是200,第一行的代码保证了重定向的发生,第二行代码指明了重定向的地址。

重定向、定时刷新和请求转发的比较

共同点:请求访问的是A,但看到的是B的内容;
               他们后续代码会继续执行,有时会造成歧义,这就好比上一位运动员以及将接力棒给下一位运动员,但上一位运动员仍然继续在跑道上前行,通常情况下,这三者代码后面要紧跟return语句,防止后续代码继续执行。


1.重定向和定时刷新的区别

(1)定时刷新有“定时”功能;重定向没有定时功能。

(2)定时刷新到新页面前,可以在当前页面输出数据(第一次响应结束后),严格的来讲,重定向不可以。

(3)重定向Location必须和状态码例如302一起使用。定时刷新的响应状态码是200

(4)定时刷新时,浏览器和web服务器要建立两次tcp/ip连接,而重定向可能只需要一次(当重定向前后访问的是同一个Web服务器的两个Servlet程序时只用建立一次TCP/IP连接,浏览器向Web服务器发送两次HTTP请求)

(5)若不使用定时功能,尽量使用重定向

2.请求转发与重定向的区别

(1)请求转发后,HTTP请求尚未处理完成,需要交由下一个Servlet程序继续处理HTTP请求直至最后一个Servlet程序返回响应。

(2)请求转发只能在同一个Web项目内进行请求的转发和呼应,重定向可以将Location设置成任意网址

(3)若请求转发和重定向都在同一Web项目内进行,那么区别是什么呢?
        请求次数和响应次数不一样
        请求转发的流程:请求A-请求转发至B-B做出响应-请求/响应结束(此时只用一个HTTP请求)
        而重定向会有两次请求,好比,我打电话给A,服务员帮我转接,结果A换电话号码了,然后我换号码打,发送新的请求(第二次一定是GET请求)最后接通

(4)第一次响应的时机不一样
        请求转发前生成的HTTP响应数据,是要被response对象的缓存延时发送到浏览器端的,直至最后的Servlet程序向浏览器返回运行结果,第一次响应结束

        重定向前生成的HTTP响应数据,会随着Location响应头和302状态码,返回浏览器第一次响应结束

        因此他们第一次响应时机的不同,导致Cookie使用也不同。

(5)表单重复提交问题不一样

请求转发意味着请求尚未结束,浏览器地址栏不会发生变化。请求转发期间,由于浏览器误认为请求尚未结束,若刷新浏览器页面,可能会导致表单重复提交的问题。而重定向则不会。

(6)若需要将POST请求转化成GET请求,必须使用重定向。
请求方式一旦确定,请求转发期间,请求类型就无法改变
重定向后第二次请求一定是GET请求

(7)数据共享方式不同
重定向:重定向的两个程序,通常借助查询字符串实现数据的共享,也可以通过Cookie或者Session实现数据的共享。
请求转发:请求转发的两个程序还可以通过请求参数以及request请求对象上绑定的属性,实现数据的共享。尤其是request请求对象的属性可以存放任意对象

(8)URL路径定位方式不同

重定向:由于重定向后,新的HTTP请求来自浏览器,因此重定向的Location若使用server-relative路径,必须包含Web项目的虚拟路径
请求转发:若使用server-relative路径,不能包含Web项目的虚拟路径


使用response响应对象生成HTTP响应体

使用response响应对象既可以将文本型数据封装到HTTP响应体中,也可以将二进制数据封装在HTTP响应体。但需要注意,HTTP响应体的数据只能是文本型数据和二进制数据二选一。ps:以后将通过实验来对知识点进行展开

response响应对象的缓存

        response缓存

向response缓存添加文本型数据

        java.io.PrintWriter        response.getWriter():返回可以将文本型数据添加到 response 缓存的 PrintWriter 对象。

        使用 PrintWriter 对象的 write()、print()、println()、或者append()方法可以将文本型数据添加到response缓存。write只能添加字符串,print可以将各种类型的数据转化成字符串后再添加到response缓存中,(回车和换行符会被html解析成空格符)append支持方法连缀。

       说明1: 默认情况下,PrintWriter将文本型数据编码成对应的ISO-8859-1编码数据,故不支持中文。

        说明2:针对上述情况建议使用如下代码:response.getCharacterEncoding("UTF-8")

注意:必须在第一次向response缓存中添加文本型数据前设置文本型数据的字符编码。

        说明3: 下面方法返回response缓存中的文本型数据的字符编码

String response.getCharacterEncoding(),和上述只少了输入参数,返回null时,表示默认为OSI-8859-1.

向response缓存添加字节型数据

        说明1:添加字节数据前,设置响应头Content-Type,提前告知浏览器,字节数据的内容格式MIME

        说明2:若字节数据全是文本型数据,刻在Content-Type响应头设置charset,指定字节数据的字符编码,如果字节数据中包含了图片、音频等二进制数据,就无须在Content-Type响应头中设置charset。

注意:response.getWriter()方法用于向response缓存添加文本型数据,response.getOutputStream()方法用于向response缓存添加字节数据,response缓存中不能同时包含二者。

response响应对象一石三鸟的代码

(1)向response缓存添加数据之前,提前设置数据的内容格式(MIME),代码如下:

response.setContentType("text/html")
注意:String response.setContentType() 该方法返回response缓存数据的内容格式,若为NULL则表示由浏览器决定响应数据的内容格式,和上一小节代码有相同之处。

(2)一石三鸟的代码

以下代码实现一石三鸟:1.将文本型数据编码成UTF-8编码的数据后,添加到response缓存2.通知浏览器,HTTP响应数据是HTML格式的文本型数据;第三通知浏览器,按照UTF-8编码解码HTTP响应数据

response.setContentType("text/html;charset=UTF-8")

注:response缓存中所有数据的字符编码都是一致的。缓存的第一条数据的字符编码决定后来数据的字符编码,必须在第一次向response缓存中添加文本型数据前设置文本型数据的字符编码,之后设置是无效的。


实践任务        Servlet生成HTTP响应数据

场景1步骤:

1. 创建动态Web项目,项目名称是response

2.展开Java Resource,右键src-->单击Servlet--> 弹出Create Servlet窗口。Java package文本框输入controller,Class name文本框处输入CBAServlet,单击“完成”按钮

3.精简代码,只保留CBAServlet.java的doGet方法,并将代码修改为如下代码,为浏览器用户提供各种功能。

package controller;

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

@WebServlet("/CBAServlet")
public class CBAServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;
       

    public CBAServlet() {
        super();
        // TODO Auto-generated constructor stub
    }


	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String action = request.getParameter("action");
		if(action == null || "".equals(action)) {
			response.setContentType("text/html;charset=UTF-8");
			response.getWriter()
			.append("本页面是程序首页,为浏览器用户提供了若干功能<br/>")
			.append("在URL后添加<font color='red'>查询字符串</font>,测试response各功能<br/>")
			.append("?action=statusCode 测试响应状态码,在Tomcat控制器查看本程序的响应状态码<br/>")
			.append("?action=header 注意使用F12,打开浏览器使用开发者工具<br/>")
			.append("?action=refresh 查看Tomcat控制台,refresh后的代码是否执行<br/>")
			.append("?action=redirect 查看Tomcat控制台,redirect后的代码是否执行<br/>")
			.append("?action=forward 查看Tomcat控制台,请求转发后的代码是否执行<br/>")
			.append("?action=include 查看Tomcat控制台,请求包含后的代码是否执行<br/>")
			.append("?action=contentType 不指定浏览器解码方式,出现乱码了吗?<br/>")
			.append("?action=chars 字符数据里不能掺杂字节数据,否则异常<br/>")
			.append("?action=bytes 字节数据里不能掺杂字符数据,否则异常<br/>")
			.append("?action=picture&fileName = 学习强国.jpg 通过response预览图片<br/>")
			.append("?action=picture&subAction=download&fileName=学习强国.jpg 通过response下载图片文件<br/>")
			.append("?action=img&filename=学习强国.jpg 使用<img>标签预览图片<br/>")
			.append("?action=imgServlet&fileName=学习强国.jpg 使用<img>标签和response的结合预览图片<br/>")
			;
			return;
		}
		System.out.println("开始测试"+action+"功能");
		//所有新增的功能代码添加到此处
		//通知浏览器用户更多功能尚未开发
		response.setContentType("text/html;charset = utf-8");
		response.getWriter().print("该功能尚未提供,期待您的开发升级!<br/>");
	}


	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
		doGet(request, response);
	}

}

运行结果如下: 

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 本实践任务编写的CBAServlet程序,可以根据不同的参数,产生不同的处理结果,关键技巧向Servlet的doGet()方法传递action查询字符串,action参数控制了Servlet程序的运行流程,必须要掌握此技巧

场景2步骤 使用response响应对象设置状态响应码

(1)向Servlet程序的doGet()方法新增如下代码

		//所有新增的功能代码添加到此处
		if(action.equals("statusCode")) {
			System.out.println("设置前的状态码是:" + response.getStatus());
			response.sendError(404,"您访问的页面“跑道火星上去”了!");
			System.out.println("设置后的状态码是:"+response.getStatus());
			return;
		}

执行结果如下:注意网址

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 技巧1:该页面是友好的错误提示页面

 技巧2:在Web部署描述符文档(Web.xml配置文件)配置错误页面,这样出错后,浏览器用户看到的不再是Web服务器提供的默认错误页面

(3)创建Web部署描述符文档

web.xml配置文件位于WEB-INF目录下,若目录没有web.xml配置文件,就右击Web项目名,选择Java EE Tools,单击Generate Deployment Descriptor Stub 即可在WEB-INF目录下创建web.xml配置文件

(4)在web.xml配置文件中配置友好的404错误提示页面

error添加后如下:watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 (5)创建友好的404错误提示页面404.jsp

打开 Project Explore视图->展开abc项目->右击WebContent目录->选择New->单击JSP File->File Name文本框处输入404.jsp->单击Next按钮->使用默认模板->单击Finish按钮->在WebContent目录下创建404.jsp程序,将该程序代码修改为如下代码。

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<!DOCTYPE html>
<html>
<head>
<title>不要着急......</title>
</head>
<body>
	<center>
		<h1>对不起,您访问的页面暂时丢失了</h1>
	</center>
</body>
</html>

 

场景3 使用response响应对象设置响应头列表

(1)向Servlet程序的doGet()方法新增如下代码

if(action.equals("header")) {
			response.setHeader("Content-Type", "text/html;charset=utf-8");
			response.setIntHeader("Content-Length", 0);
			response.setDateHeader("Date", System.currentTimeMillis());
			System.out.println("包含Set-Cookie响应头吗?"+response.containsHeader("Set-Cookie"));
			response.addHeader("Set-Cookie", "username=zhangsan;path=/;HttpOnly");
			response.addHeader("Set-Cookie", "password=123456;path=/;HttpOnly");
			System.out.println("包含Set-Cookie响应头吗?"+response.containsHeader("Set-Cookie"));
			System.out.println("Set-Cookie响应头是"+response.getHeader("Set-Cookie"));
			java.util.Collection<java.lang.String> headerNames = response.getHeaderNames();
			for(String headerName : headerNames) {
				System.out.print(headerName + "是:");
				java.util.Collection<java.lang.String> headerValues = response.getHeaders(headerName);
				for(String headerValue:headerValues) {
					System.out.println(headerValue);
				}
			}
			return;
		}

(2)输入网址 localhost:8080/response/CBAServlet?action=header 打开F12开发者模式,先F12再开网址

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 

场景4步骤 重定向、定时刷新、请求转发、请求包含等的使用以及文本型数据封装到响应体

上一章节的form0.jsp复制到WebContent目录

doGet()方法新增如下代码

if(action.equals("refresh")) {
			//定时刷新,是浏览器端发出的HTTP请求 同下重定向,需要完整虚拟路径
			//会先出字,然后跳转到form0.jsp
			response.setHeader("Content-Type", "text/html;charset=utf-8");
			String url = request.getContextPath() + "/" + "form0.jsp";
			response.getWriter().append("观察refresh前的字符串是否输出到网页上");
			response.setHeader("refresh", "5;url=" + url);
			response.getWriter().append("观察refresh后的字符串是否输出到网页上");
			System.out.println("refresh后的语句");
			return;
			
		}
		if(action.equals("redirect")) {//重定向不需要设置字符编码,原因是重定向第一次仅包含Location响应头
			//和302响应状态码,第一次响应没有HTTP响应体,而定时刷新第一次响应可以包含HTTP响应体
			String url = request.getContextPath() + "/" + "form0.jsp";
			 response.getWriter().append("观察 redirect 前的字符串是否输出到网页上!"); 
			 response.sendRedirect(url);
			 response.getWriter().append("观察 redirect 后的字符串是否输出到网页上!");
			 System.out.println("redirect后的语句"); 
			 return ;
		}

		if(action.equals("forward")) {
		 response.setHeader ("Content-Type","text/html;charset=UTF-8");
		 response.getWriter().append("观察 forward 前的字符串是否输出到网页上!"); 
		 request.getRequestDispatcher("/form0.jsp").forward (request,response); 
		 response.getWriter().append("观察 forward 后的字符串是否输出到网页上!"); 
		 System.out.println("forward 后的语句");
		 return ;
		}
		 if(action.equals ("include")){
		 response.setHeader("Content-Type","text/html;charset=UTF-8");
		 response.getWriter().append ("观察 include 前的字符串是否输出到网页上!"); 
		 request.getRequestDispatcher("/form0.jsp").include(request,response) ;
		 response.getWriter().append ("观察 include 后的字符串是否输出到网页上!"); 
		 System.out.println ("include 后的语句");
		 return ;
		 }
		 if(action.equals("chars")) {
			 response.setContentType("text/html;charset=utf-8");
			 response.getWriter().write("write数据");
			 //response.getWriter()返回的是PrintWriter,这是一个打印输出流
			 //response.getWriter().writer(),只能打印输出文本格式的(包括html标签),不可以打印对象
			 response.getWriter().print(2020);
			 response.getWriter().print("print数据");
			 java.util.List<String> l = new java.util.ArrayList<String>();
			 l.add("test");
			 response.getWriter().print(l);
			 response.getWriter().println("println数据");
			 response.getWriter().append("append数据1")
			 .append("append数据2")
			 .append("append数据3");
			 String data = "使用OutputStream将字节数据封装到响应体<br/>";
			 byte[] dataByteArray = data.getBytes("utf-8");
			 response.getOutputStream().write(dataByteArray);
			 return;
		 }
        if(response.equals("bytes")) {
			 response.setContentType("text/html;charset=utf-8");
			 String data = "使用OutputStream将字节数据封装到响应体";
			 byte[] dataByteArray = data.getBytes("utf-8");
			 response.getOutputStream().write(dataByteArray);
			 response.getOutputStream().write(dataByteArray,0,dataByteArray.length);
			 response.getWriter().write("write数据");//字节数据封装到响应体之后,不能再封装字符
			 return;
		 }

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 

场景5使用Servlet的response响应对象,在浏览器页面中显示图片或者下载图片

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

步骤:代码如下

if(action.equals("picture")) {
			 String fileName = request.getParameter("fileName");
			 String path = this.getServletContext().getRealPath("download/" + fileName);
			 java.io.File file = new java.io.File(path);
			 java.io.FileInputStream fis = new java.io.FileInputStream(file);
			 String mime = request.getServletContext().getMimeType(fileName);
			 if(mime == null) {
				 mime = "application/octet-stream";
			 }
			 response.setContentType(mime);
			 response.setContentLength((int)file.length());
			 String subAction = request.getParameter("subAction");
			 if(subAction != null && subAction.equals("download")) {
				 System.out.println("开始测试" + subAction + "功能");
				 //解决下载时文件名乱码问题
				 String newFileName = new String(fileName.getBytes("UTF-8"),"ISO-8859-1");//不理解
				 response.setHeader("Content-Disposition", "attachment;fileName=" + newFileName);
			 }
			 javax.servlet.ServletOutputStream sos = response.getOutputStream();
			 byte[] bytes = new byte[1024*4];
			 int len = 0;
			 while((len = fis.read(bytes))!=-1){
				 sos.write(bytes,0,len);
			 }
			 fis.close();
			 sos.close();
			 return;
		 }

创建download文件夹,然后放素材进去 

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_9,color_FFFFFF,t_70,g_se,x_16

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 

 然后是下载:

?action=picture&subAction=download&fileName=学习强国.jpg 通过response下载图片文件

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 

 场景7 向Servlet程序的doGet()方法新增如下代码

		 if(action.equals("img")) {
			 String fileName = request.getParameter("fileName");
			 String contextPath = request.getContextPath();
			 String src = contextPath + "/download/" + fileName;
			 String img = "<img src='" + src + "' />";//先src内部地址搞出来,再用单括号括起来
			 response.setContentType("text/html;charset=utf-8");
			 response.getWriter().append("图片和文字共存:<br/>").append("img");
			 return;
		 }

watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

场景8<img>标签结合response缓存,在浏览器显示图片

(1)向Servlet程序的doGet()方法新增如下代码

if(action.equals("imgServlet")) {
			 String fileName = request.getParameter("fileName");
			 String contextPath = request.getContextPath();//获取项目根路径
			 String servletURLPattern = request.getHttpServletMapping().getPattern();
			 //Servlet 映射是从HttpServletRequest实例获得的
			 //getPattern()—返回激活 servlet 请求的 URL 模式
			 String src = contextPath + servletURLPattern + "?action=picture&fileName=" + fileName;
			 String img = "<img src='" + src + "' />";
			 response.setContentType("text/html;charset=utf-8");
			 response.getWriter().append("图片和文字共存:并且图片的物理路径可以是任意位置:<br/>").append(img);
			 return;
		 }

 watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5Y-v6Z2g55qE5oiQ5bm055S35a2Q,size_20,color_FFFFFF,t_70,g_se,x_16

 需要注意的是,第一次浏览器接收到<img>标签后,紧接着向CBAServlet发送了第二次请求,第二次请求的url是:localhost:8080/response/CBAServlet?action=picture&fileName=JAVAEE 开发.jpg

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

可靠的成年男子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值