一、EL表达式
1、什么是EL表达式,EL表达式的作用?
EL表达式的全称是:Expression Language,是表达式语言。
EL表达式的作用:EL表达式主要是替代jsp页面中的表达式脚本在jsp页面中进行数据的输出。
因为EL表达式在输出数据的时候,要比jsp的表达式脚本要简洁很多。
EL表达式的格式是:${表达式}
EL表达式在输出null值的时候,输出的是空串,jsp表达式脚本输出null值得时候,输出的是null字符串。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<%
request.setAttribute("key","值");
%>
表达式脚本输出的key的值是:<%=request.getAttribute("key1")==null?"":request.getAttribute("key1")%><br/>
EL表达式输出key的值是:${key1}
</body>
</html>
2、EL表达式搜索域数据的顺序
EL表达式主要是在jsp页面中输出数据,主要是输出域对象中的数据。
当四个域中都有相同的key的数据的时候,EL表达式会按照四个域的从小到大的顺序去进行搜索,找到就输出。
<body>
<%
//往四个域中都保存了相同的key的数据
pageContext.setAttribute("key","pageContext");
request.setAttribute("key","request");
session.setAttribute("key","session");
application.setAttribute("key","application");
%>
${key}
</body>
3、EL表达式输出Bean的普通属性,数组属性。List集合属性,map集合属性
查找的不是Bean类中的属性,而是该属性对应的get方法。
需求:
输出Person类中普通属性,数组属性。List集合属性和map集合属性。
<body>
<%
Person person = new Person();
person.setName("阿");
person.setPhones(new String[]{"1888888888","100000000","1999999999"});
ArrayList<String> cities = new ArrayList<>();
cities.add("北京");
cities.add("上海");
cities.add("广州");
cities.add("深圳");
person.setCities(cities);
HashMap<String, Object> map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
map.put("key4","value4");
person.setMap(map);
// 把person放入进域中
pageContext.setAttribute("per",person);
%>
输出person:${ per }<br/>
输出person的name属性:${ per.name }<br/>
输出person的phones数组属性值:${ per.phones[2] }<br/>
输出person的cities集合中的元素值:${ per.cities }<br/>
输出person的cities集合中的某一个元素值:${ per.cities[0] }<br/>
输出person的map集合中的元素值:${ per.map }<br/>
输出person的map集合中的某一个元素值:${ per.map.key1 }<br/>
<%--下面的可以实现同样的输出,对于含有特殊字符,例如“。” --%>
输出person的map集合中的某一个元素值:${ per.map["key1"] }<br/>
</body>
4、EL表达式- - -运算
语法:${ 运算表达式 },EL表达式支持如下运算符:
补:
4) empty运算:
empty运算可以判断一个数据是否为空,如果为空,则输出true,不为空输出false。
以下几种情况为空:
① 值为null值得时候
② 值为空串得时候
③ 值是Object类型数组,长度为零得时候
④ list集合,元素个数为零
⑤ map集合,元素个数为零
<body>
<%
// ① 值为null值得时候
request.setAttribute("emptyNull",null);
// ② 值为空串得时候
request.setAttribute("emptyStr","");
// ③ 值是Object类型数组,长度为零得时候
request.setAttribute("emptyArr",new Object[]{});
// ④ list集合,元素个数为零
ArrayList<Object> list = new ArrayList<>();
request.setAttribute("emptyList",list);
// ⑤ map集合,元素个数为零
HashMap<String, Object> map = new HashMap<>();
request.setAttribute("emptyMap",map);
%>
${ empty emptyNull } <br/>
${ empty emptyStr } <br/>
${ empty emptyArr } <br/>
${ empty emptyList } <br/>
${ empty emptyMap } <br/>
</body>
5)三元运算:
表达式1 ? 表达式2:表达式3
如果表达式1得值为真,返回表达式2的值,如果表达式1的值为假,返回表达式3的值。
6)".“点运算和”[]"中括号运算符:
.运算,可以输出Bean对象中某个属性的值;
[]运算,可以输出有序集合中某个元素的值;
并且[]运算符,还可以输出map集合中key里含有特殊字符的key的值。
<body>
<%
HashMap<String, Object> map = new HashMap<>();
map.put("aaa","aaaValue");
map.put(".bbb","bbbValue");
map.put("!c+c*c","cccValue");
request.setAttribute("map",map);
%>
${ map.aaa} <br/>
<%--使用第二种输出方式---[],避免识别错误,不能输出的问题--%>
${ map[".bbb"]} <br/>
${ map["!c+c*c"]} <br/>
</body>
5、EL表达式的11个隐含对象
EL表达式种11个隐含对象,是EL表达式种自己定义的,可以直接使用。
1)EL获取四个特定域中的属性
<body>
<%
pageContext.setAttribute("key1","pageContext1");
pageContext.setAttribute("key2","pageContext2");
request.setAttribute("key2","request2");
session.setAttribute("key2","session2");
application.setAttribute("key2","application2");
%>
<%--只选择key2会,优先输出范围最小的一个,即:pageContext2--%>
${ pageScope.key2 } <br/>
${ requestScope.key2 }<br/>
${ sessionScope.key2 }<br/>
${ applicationScope.key2 }<br/>
</body>
2)pageContext对象的使用
<body>
<%--
request.getScheme()获取请求的协议
request.getserverName()获取请求的服务器ip或域名
request.getserverPort()获取请求的服务器端口号
request.getContextPath()获取当前工程路径
request.getMethod()获取请求的方法(GET/Post)
request.getRemoteHost()获取客户端的ip地址
session.getid()获取会话的id编号
--%>
<%=request.getScheme()%> <br/>
<%--默认找getxxx方法--%>
1、协议:${ pageContext.request.scheme} <br/>
2、服务器ip:${ pageContext.request.serverName} <br/>
3、服务器端口: ${ pageContext.request.serverPort} <br/>
4、获取工程路径:${ pageContext.request.contextPath} <br/>
5、获取请求方法: ${ pageContext.request.method} <br/>
6、获取客户端ip地址: ${ pageContext.request.remoteHost} <br/>
7、获取会话的id编号: ${ pageContext.session.id} <br/>
</body>
3)param、paramValues对象获取请求参数的使用
paramValues获取的是某一个对象的数组,要输出值,需要索引。(用于多个值)
<body>
输出请求参数username的值:${ param.username }<br/>
输出请求参数username的值:${ paramValues.username[0] }<br/>
</body>
请求地址栏:
http://localhost:8080/09_EL_JSTL/other_el_obj.jsp?username=168
输出:
输出请求参数username的值:168
输出请求参数username的值:168
4)header、paramValues对象获取请求头的使用
<body>
输出请求头【User-Agent】的值:${header["User-Agent"]}<br/>
输出请求头【Connection】的值:${header.Connection}<br/>
输出请求头【User-Agent】的值:${headerValues["User-Agent"][0]}<br/>
</body>
请求地址栏:
http://localhost:8080/09_EL_JSTL/other_el_obj.jsp?username=168
输出:
输出请求头【User-Agent】的值:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36
输出请求头【Connection】的值:keep-alive
输出请求头【User-Agent】的值:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36
5)cookie对象的使用
<body>
<%--cookie.JSESSIONID获取cookie对象--%>
${ cookie }<br/>
${ cookie.JSESSIONID }<br/>
获取Cookie的名称:${ cookie.JSESSIONID.name }<br/>
获取Cookie的值:${ cookie.JSESSIONID.value }
</body>
请求地址栏:
http://localhost:8080/09_EL_JSTL/other_el_obj.jsp?username=168
输出:
{JSESSIONID=jakarta.servlet.http.Cookie@387b7001, Idea-1012b08a=jakarta.servlet.http.Cookie@28d30e0c}
jakarta.servlet.http.Cookie@387b7001
获取Cookie的名称:JSESSIONID
获取Cookie的值:B16A363CAD52B4CB23D308D9A9AC0175
6)initParam对象的使用
<body>
${ initParam }<br/>
输出<Context-param>username的值:${ initParam.username }<br/>
输出<Context-param>url的值:${ initParam.url}<br/>
</body>
请求地址栏:
http://localhost:8080/09_EL_JSTL/other_el_obj.jsp?username=168
输出:
{url=jdbc:mysql:///test, username=root}
输出<Context-param>username的值:root
输出<Context-param>username的值:jdbc:mysql:///test
二、JSTL标签库
JSTL标签库,全称是指JSP Standard Tag Library 是一个不断完善的开放源代码的JSP标签库。
EL表达式主要是为了替换jsp中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个jsp页面更加简洁。
2.1 JSTL标签库的使用步骤
1)先导入jstl标签库的jar包。
taglibs-standard-impl-1.2.1.jar
taglibs-standard-spec-1.2.1.jar
Tomcat10版本是需要导入下面两个:
jakarta.servlet.jsp.jstl-2.0.0.jar
jakarta.servlet.jsp.jstl-api-2.0.0.jar
2)第二部,使用taglib指令引入标签库
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
2.2 core核心库使用
1)<c:set/>
作用:set标签可以往域中保存数据
<body>
<%--
<c:set/>
作用:set标签可以往域中保存数据
域对象.setAttribute(key,value);
scope属性设置保存到哪个域
page表示PageContext(默认值)
request表示Request
session表示Session
application表示ServletContext
var属性设置key是多少
value属性设置值
--%>
保存之前:${requestScope.abc}<br/>
<c:set scope="request" var="abc" value="abcValue"/>
保存之后:${requestScope.abc}<br/>
</body>
2)<c:if/>
作用:用来作判断
<body>
<%--if标签作判断,test属性表示判断的条件(使用EL表达式输出)--%>
<c:if test="${12==12}">
<h1>12等于12</h1>
</c:if>
</body>
3)<c:choose> <c:when> <c:otherwise>
作用:多路判断,跟switch...case...default非常接近
<body>
<%--<c:choose> <c:when> <c:otherwise>
作用:多路判断,跟switch...case...default非常接近
choose标签开始选择判断
when标签标识每一种判断情况
test属性表示当前这种判断情况的值
otherwise标签标识剩下的情况
--%>
<%
request.setAttribute("height","185");
%>
<c:choose>
<c:when test="${requestScope.height>190}">
<h2>高的呀</h2>
</c:when>
<c:when test="${requestScope.height>180}">
<h2>一般般</h2>
</c:when>
<c:when test="${requestScope.height>170}">
<h2>海星</h2>
</c:when>
<c:when test="${requestScope.height>160}">
<h2>布星</h2>
</c:when>
<c:otherwise>
<h2>啊这...</h2>
</c:otherwise>
</c:choose>
</body>
4)<c:forEach/>
作用:遍历输出使用
① 遍历输出1到10
<body>
<%--1、遍历输出1到10
begin属性设置开始的索引
end属性设置结束的索引
var属性表示循环的变量(也是当前正在遍历到的数据)
for(int i=1;i<10;i++)
--%>
<c:forEach begin="0" end="10" var="i">
${ i }
</c:forEach>
<table border="1" bgcolor="#adff2f">
<c:forEach begin="0" end="10" var="i">
<tr>
<td>第${i}行</td>
</tr>
</c:forEach>
</table>
</body>
② 遍历Object数组
<body>
<%--2、遍历Object数组
for(Object:arr)
item 表示遍历的数据源(便利的集合)
var表示当前遍历到的数据
--%>
<%
request.setAttribute("arr",new String[]{"11111111111","22222222222","33333333333"});
%>
<c:forEach items="${requestScope.arr}" var="item">
${item} <br/>
</c:forEach>
</body>
③ 遍历Map集合
<body>
<%
HashMap<String, Object> map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
//for (Map.Entry<String,Object> entry:map.entrySet()){
//}
request.setAttribute("map",map);
%>
<c:forEach items="${requestScope.map}" var="entry">
<h5>${entry}</h5>
<h4>${entry.key}</h4>
<h3>${entry.value}</h3>
</c:forEach>
</body>
④ 遍历List集合- - -list中存放Student类,有属性:编号,用户名,密码,年龄,电话信息
<body>
<%--遍历List集合- - -list中存放Student类,有属性:编号,用户名,密码,年龄,电话信息--%>
<%
ArrayList<Student> studentList = new ArrayList<>();
for (int i=0;i<10;i++){
studentList.add(new Student(i,"username"+i,"pass"+i,18+i,"phone"+i));
}
request.setAttribute("stus",studentList);
%>
<table>
<tr>
<th width="1">编号</th>
<th>用户名</th>
<th>密码</th>
<th>年龄</th>
<th>电话</th>
<th>操作</th>
</tr>
<c:forEach begin="2" end="7" step="2" varStatus="status" items="${requestScope.stus}" var="stu">
<tr>
<td>${stu.id}</td>
<td>${stu.name}</td>
<td>${stu.password}</td>
<td>${stu.age}</td>
<td>${stu.phone}</td>
<td>${status}</td>
</tr>
</c:forEach>
</table>
</body>
forEach总结:
items表示遍历的数据源
begin属性设置开始的索引
end属性设置结束的索引
var属性表示循环的变量(也是当前正在遍历到的数据)
step属性表示遍历的步长值
varStatus属性表示当前遍历到的数据的状态
Status实现了LoopTagStatus接口,故也具有以下方法。
三、文件的上传和下载
3.1 文件的上传介绍(重点)
① 要有一个form标签,method=post请求
② form标签的encType属性值必须为multipart/form-data(表示提交的数据,以多段[每一个表单项是一个数据段]的形式进行拼接,然后以二进制流的形式发送给服务器)
③ 在form标签中使用input type=file添加上传的文件
④ 编写服务器代码(Servlet程序)接收,处理上传的数据
upload,jsp代码如下:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="http://localhost:8080/09_EL_JSTL/uploadServlet" method="post" enctype="multipart/form-data">
用户名:<input type="text" name="username"><br/>
头像:<input type="file" name="photo"><br/>
<input type="submit" value="上传">
</form>
</body>
</html>
UploadServlet服务器程序接受、处理上传的数据:
public class UploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
//用来处理上传的数据
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//System.out.println("文件上传过来了");
//客户端是流的形式进行发送的,因此只能以流的形式进行接受
ServletInputStream inputStream = request.getInputStream();
// 利用缓冲区一次读到
byte[] buffer = new byte[1024000];
int read = inputStream.read(buffer);
System.out.println(new String(buffer,0,read));
}
}
3.2 commons-fileupload.jar常用API介绍说明
commons-fileupload.jar需要依赖commons-io.jar这个包,所以两个包都要引入。
包内常用类:
ServletFileUpload类,用于解析上传的数据
FileItem类,表示每一个表单项
解析收到的内容:
public class UploadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
//用来处理上传的数据
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//1、先判断上传的数据是否多段数据(只有多段的数据,才是文件上传的)
if (ServletFileUpload.isMultipartContent(request)){
// 创建FileItemFactory工厂实现类
DiskFileItemFactory fileItemFactory = new DiskFileItemFactory();
// 创建用于解析上传数据的工具类ServletFileUpload类
ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
//解析上传的数据,得到每一个表单项FileItem
try {
List<FileItem> list = servletFileUpload.parseRequest((RequestContext) request);
//循环判断,每一个表单项,是普通类型,还是上传的文件
for (FileItem fileItem:list){
if (fileItem.isFormField()){
//普通表单项
System.out.println("表单项的name属性值:"+fileItem.getFieldName());
// 参数UTF-8解决乱码问题
System.out.println("表单项的value属性值:"+fileItem.getString("UTF-8"));
}else {
//上传的文件
System.out.println("表单项的name属性值:"+fileItem.getFieldName());
System.out.println("上传的文件名:"+fileItem.getName());
try {
fileItem.write(new File("d:\\"+fileItem.getName()));
} catch (Exception e) {
e.printStackTrace();
}
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
}
}
3.3 文件的下载介绍(重点)
具体流程:
其中用到了commons-io-2.8.0.jar包中的IOUtils类来方便读取数据。
注意
还要告诉客户端收到的数据是用于下载使用(还是使用响应头)
// Content-Disposition响应头,表示收到的数据怎么处理
// attachment表示附件(下载使用)
//filename=表示指定下载的文件名
//url编码是把汉字转换成为%xx%xx的格式(十六进制)IE和谷歌用的是UTF-8编码,
public class Download extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)throws SecurityException, IOException{
//1、获取要下载的文件名
String downloadFileName="6.jpg";
//2、读取要下载的文件内容(通过ServletContext对象可以读取)
ServletContext servletContext=getServletContext();
// 获取要下载的文件类型
String mimeType = servletContext.getMimeType("/file/" + downloadFileName);
System.out.println("下载的文件类型"+mimeType);
//4、在回传前,通过响应头告诉客户端返回的数据类型
response.setContentType(mimeType);
//5、还要告诉客户端收到的数据是用于下载使用(还是使用响应头)
// Content-Disposition响应头,表示收到的数据怎么处理
// attachment表示附件(下载使用)
//filename=表示指定下载的文件名
//url编码是把汉字转换成为%xx%xx的格式(十六进制)
response.setHeader("Content-Disposition","attachment;filename="+ URLEncoder.encode("真好.jpg","UTF-8"));
/* /斜杠被服务器解析为http://ip:port/工程名/ 映射到代码的web目录*/
InputStream resourceAsStream = servletContext.getResourceAsStream("/file/" + downloadFileName);
//3、把下载的文件内容回传给客户端
//获取相应的输出流
ServletOutputStream outputStream = response.getOutputStream();
//读取输入流中的全部数据,赋值给输出流,输出给客户端
IOUtils.copy(resourceAsStream,outputStream);
}
}
火狐浏览器要使用BASE64编解码处理:
举例操作如下:
具体实现内容如下: