EL表达式&JSTL标签&文件上传下载
一、EL表达式
1.1EL表达式概述
- EL 表达式的全称是:Expression Language.是表达式语言。
- EL表达式的作用:EL表达式主要是代替jsp页面中的表达式脚本在jsp页面中进行数据的输出
- 因为EL表达式在输出数据的时候,要比jsp的表达式脚本要简洁很多。
<% request.setAttribute("KEY","值");%>
<!-- 表达式脚本输出key的值是:-->
<%=request.getAttribute("KEY")==null?"":request.getAttribute("KEY")%><br>
${s1}<br>
${KEY}<br>
- EL表达式的格式是:${表达式}
- EL表达式在输出null值的时候,输出的是空串。jsp表达式脚本输出null值的时候,输出的是null字符串。
1.2 EL表达式搜索域数据的顺序
EL 表达式主要是在jsp页面输出数据。
主要是输出域对象中的数据。
当四个域中都有相同的key的数据的时候,EL表达式会按照四个域的从小到大的顺序去进行搜索,找的就输出。
<%
// request.setAttribute("key","request");2
// session.setAttribute("key"," session");3
// application.setAttribute("key","application");4
// pageContext.setAttribute("key","pageContext");1
%>
${key}
1.3 EL表达式输出Bean的普通属性,数组属性。List集合属性,map集合属性
- 输出Person类中普通属性,数组属性。List集合属性和map集合属性。
Person类
package Bean;
import java.util.List;
import java.util.Map;
public class Person {
private String name;
private String[] phones;
private List<String> cities;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String[] getPhones() {
return phones;
}
public void setPhones(String[] phones) {
this.phones = phones;
}
public List<String> getCities() {
return cities;
}
public void setCities(List<String> cities) {
this.cities = cities;
}
public Map<String, Object> getMap() {
return map;
}
public void setMap(Map<String, Object> map) {
this.map = map;
}
private Map<String,Object>map;
public int getAge(){
return 18;
}
}
输出的代码
<%
Person person = new Person();
person.setName("王超666");
person.setPhones(new String[]{"12345647896","1254478965","15344599282"});
ArrayList<String> cities = new ArrayList<>();
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);
pageContext.setAttribute("p",person);
%>
输出Person:${p}<br>
输出Person的name属性:${p.name}<br>
输出Person的phones数组的属性值:${p.phones[1]}<br>
输出Person的phones数组的属性值:${p.phones}<br>
输出Person的cities数组的元素值:${p.cities}<br>
输出Person的Map集合:${p.map};<br/>
输出Person的Map集合中某个key的值:${p.map.key3}<br>
输出Person的age属性:${p.age};<br/>
1.4 EL 表达式运算
语法:${运算表达式},EL表达式支持如下运算符:
关系运算
关系运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
==或eq | 等于 | 5 = = 5 或 {5==5}或 5==5或{5 eq 5} | true |
!=或ne | 不等于 | 5 ! = 5 或 {5!=5}或 5!=5或{5 ne 5 } | false |
<或lt | 小于 | 3 < 5 或者 {3<5}或者 3<5或者{3 lt 5} | true |
>或gt | 大于 | 2 > 10 或 {2>10}或 2>10或{2 gt 10} | false |
<=或le | 小于等于 | 5 < = 12 或 {5<=12}或 5<=12或{5 le 12} | true |
>=或ge | 大于等于 | 3 > = 5 或 {3>=5}或 3>=5或{3 ge 5} | false |
逻辑运算符
逻辑运算符 | 说明 | 示例 | 结果 |
---|---|---|---|
&&或and | 与运算 | KaTeX parse error: Expected '}', got '&' at position 8: {12==12&̲&12<11}或{1 2= =12and 12<11} | false |
||或or | 或运算 | ${12==12||12<11} 或 ${ 12 = =or 12 < 11} | true |
!或not | 取反运算 | ! t r u e 或 {!true}或 !true或{not true} | false |
empty运算(常用)
empty运算可以判断一个数据是否为空,如果为空,则输出true,不为空输出false。
- 以下几种情况为空:
- 1、值为null值的时候,为空
- 2、值为空串的时候,为空
- 3、值是Object类型数组,长度为零的时候
- 4、list集合,元素个数为零
- 5、map集合,元素个数为零
//1.值为null时,为空
request.setAttribute("emptyNull",null);
//2.值为null时,为空
request.setAttribute("emptyStr","");
//3.值是Object类型数组,长度为零的时候
request.setAttribute("emptyArr",new Object[]{});
//4.list集合,元素个数为零
ArrayList<String> list = new ArrayList<>();
list.add("123");
request.setAttribute("emptyList",list);
//5.map集合,元素个数为零
HashMap<String, Object> map = new HashMap<>();
// map.put("123",2);
request.setAttribute("emptyMap",map);
%>
${empty emptyNull}<br>
${empty emptyStr}<br>
${empty emptyArr}<br>
${empty emptyList}<br>
${empty emptyMap}
<!--
true
true
true
false
true
-->
三元运算
- 表达式1?表达式2:表达式3
- 如果表达式1的值为真,返回表达式2的值,如果表达式1的值为假,返回表达式3的值
${12==12?“正确”:''错误"}
“.”运算和[]中括号运算符
.点运算,可以输出Bean对象中某个属性的值。
[]中括号运算,可以输出有序集合中某个元素的值。
并且[]中括号运算,还可以输出map集合中key里含有特殊字符的key的值。
<body>
<%
Map<String,Object>map=newHashMap<String,Object>();
map.put("a.a.a","aaaValue");
map.put("b+b+b","bbbValue");
map.put("c-c-c","cccValue");
request.setAttribute("map",map);
%>
${map['a.a.a']}<br>
${map["b+b+b"]}<br>
${map['c-c-c']}<br>
</body>
1.5 El表达式的11个隐含对象
变量 | 类型 | 作用 |
---|---|---|
pageContext | PageContextImpl | 它可以获取jsp中的九大内置对象 |
pageScope | Map<String,Object> | 它可以获取pageContext域中的数据 |
requestScope | Map<String,Object> | 它可以获取Request域中的数据 |
sessionScope | Map<String,Object> | 它可以获取Session域中的数据 |
applicationScope | Map<String,Object> | 它可以获取ServletContext域中的数据 |
param | Map<String,String> | 它可以获取请求参数的值 |
paramValues | Map<String,String[]> | 它也可以获取请求参数的值,获取多个值的时候使用。 |
header | Map<String,String> | 它可以获取请求头的信息 |
headerValues | Map<String,String[]> | 它可以获取请求头的信息,它可以获取多个值的情况 |
cookie | Map<String,Cookie> | 它可以获取当前请求的Cookie信息 |
initParam | Map<String,String> | 它可以获取在web.xml中配置的< context-param>上下文参 |
pageContext对象的使用
- 1.协议:
- 2.服务器ip:
- 3.服务器端口:
- 4.获取工程路径:
- 5.获取请求方法:
- 6.获取客户端ip地址:
- 7.获取会话的id编号:
<%--
request.getScheme()它可以获取请求的协议
request.getServerName()获取请求的服务器ip或域名
request.getServerPort()获取请求的服务器端口号
getContextPath()获取当前工程路径
request.getMethod()获取请求的方式(GET或POST)
request.getRemoteHost()获取客户端的ip地址
session.getId()获取会话的唯一标识
--%>
<%
pageContext.setAttribute("req",request);
%>
<%=request.getScheme()%><br>
1.协议:${req.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>
EL表达式其他隐含对象的使用
变量 | 类型 | 作用 |
---|---|---|
param | Map<String,String> | 他可以获取请求参数的值 |
paramValues | Map<String,String[]> | 他也可以获取请求参数的值,获取多个值的时候使用 |
输出请求参数username的值:${param.username}<br>
输出请求参数password的值:${param.password}<br>
输出请求参数username的值:${paramValues.username[0]}<br>
输出请求参数hobby的值:${paramValues.hobby[0]}<br>
输出请求参数hobby的值:${paramValues.hobby[1]}<br>
二、JSTL标签库
1. 概述
JSTL标签库全称是指JSPStandardTagLibraryJSP标准标签库。是一个不断完善的开放源代码的JSP标签库。
EL表达式主要是为了替换jsp中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个jsp页面变得更佳简洁
在jsp标签库中使用taglib指令引入标签库CORE标签库
< %@taglibprefix="c"uri=“http://java.sun.com/jsp/jstl/core”%>
XML标签库
< %@taglibprefix="x"uri=“http://java.sun.com/jsp/jstl/xml”%>
FMT标签库
< %@taglibprefix="fmt"uri=“http://java.sun.com/jsp/jstl/fmt”%>
SQL标签库
< %@taglibprefix="sql"uri=“http://java.sun.com/jsp/jstl/sql”%>
FUNCTIONS标签库
< %@taglibprefix="fn"uri=“http://java.sun.com/jsp/jstl/functions”%>
1.1JSTL标签库的使用步骤
1、先导入jstl标签库的jar包。
taglibs-standard-impl-1.2.1.jar
taglibs-standard-spec-1.2.1.jar
2、第二步,使用taglib指令引入标签库。< %@taglibprefix="c"uri=“http://java.sun.com/jsp/jstl/core”%>
2.core核心库的使用
2.1 < c:if/>
- 作用:用来做if判断
<%--<c:if/>if标签用来做if判断。test属性表示判断的条件(使用EL表达式输出)--%>
<c:if test="${12==12}">
<h1>正确</h1>
</c:if>
<c:if test="${12!=12}">
<h1>错误</h1>
</c:if>
2.2< c:choose>< c:when>< c:otherwise>标签
- 作用:多路判断。跟switch…case…default非常接近
<%-- choose标签开始选择判断
when标签表示每一种判断情况
test属性表示当前这种判断情况的值
otherwise标签表示剩下的情况
<c:choose><c:when><c:otherwise>标签使用时需要注意的点:
1、标签里不能使用html注释,要使用jsp注释
2、when标签的父标签一定要是choose标签
--%>
<%request.setAttribute("height",120);%>
<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:otherwise>
<c:choose>
<c:when test="${requestScope.height>160}">
<h3>大于160</h3>
</c:when>
<c:when test="${requestScope.height>150}">
<h3>大于150</h3>
</c:when>
<c:when test="${requestScope.height>140}">
<h3>大于140</h3>
</c:when>
<c:otherwise>其他小于140</c:otherwise>
</c:choose>
</c:otherwise>
</c:choose>
2.3< c:forEach/>
- 遍历输出
遍历1到10
<%--begin属性设置开始的索引
end属性设置结束的索引
var属性表示循环的变量(也是当前正在遍历到的数据)
--%>
<c:forEach begin="1" end="10" var="i">
<tr>
<td>第${i}行</td><br>
</tr>
</c:forEach>
遍历Object数组
<%-- for(Objectitem:arr)
items表示遍历的数据源(遍历的集合)
var表示当前遍历到的数据
--%>
<%
request.setAttribute("arr",new String[]{"185655615156","1925645132","17165156651"});
%>
<c:forEach items="${requestScope.arr}" var="item">
${item}<br>
</c:forEach>
遍历Map集合
<%
HashMap<String, Object> map = new HashMap<>();
map.put("key1","value1");
map.put("key2","value2");
map.put("key3","value3");
map.put("key4","value4");
request.setAttribute("map",map);
%>
<c:forEach items="${requestScope.map}" var="entry">
<h1 style="color: brown">${entry.key}=${entry.value}</h1>
</c:forEach>
遍历List集合
Student类:
public class Student {
private Integer id;
private String username;
private String password;
private Integer age;
private String phone;
@Override
public String toString() {
return "Student{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", phone='" + phone + '\'' +
'}';
}
public Integer getId() {
return id;
}
public void setId(Integer 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;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public Student() {
}
public Student(Integer id, String username, String password, Integer age, String phone) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.phone = phone;
}
}
代码
<!-- 遍历学生List集合-->
<%
ArrayList<Student> studentList = new ArrayList<>();
for (int i = 1; i <10 ; i++) {
studentList.add(new Student(i,"username"+i,"pass"+i,18+i,"phone"+i));
}
request.setAttribute("stus",studentList);
%>
<table>
<tr>
<th>编号</th>
<th>用户名</th>
<th>密码</th>
<th>年龄</th>
<th>电话</th>
<th>操作</th>
</tr>
<%--
items表示遍历的集合
var表示遍历到的数据
begin表示遍历的开始索引值
end表示结束的索引值
step属性表示遍历的步长值
varStatus属性表示当前遍历到的数据的状态
for(inti=1;i<10;i+=2)
--%>
<c:forEach begin="2" end="9" step="1" varStatus="status" items="${requestScope.stus}" var="stu">
<tr>
<td>${stu.id}</td>
<td>${stu.username}</td>
<td>${stu.password}</td>
<td>${stu.age}</td>
<td>${stu.phone}</td>
<td>${status.step}</td>
</tr>
</c:forEach>
</table>
三、文件的上传和下载
3.1文件上传
文件上传:将客户端的文件 保存到服务器端
1、要有一个form标签,method=post请求
2、form标签的encType属性值必须为multipart/form-data值
- application/x-www-form-urlencoded:数据进行URL编码 格式:键=值&键=值
- text/plain:纯文本发送
- multipart/form-data:可以发送二进制数据,专门用于文件上传;表示提交的数据,以多段(每一个表单项一个数据段)的形式进行拼接,然后以二进制流的形式发送给服务器
3、在form标签中使用inputtype=file添加上传的文件
4、编写服务器代码(Servlet程序)接收,处理上传的数据。
对Servlet限制:
1.request.getParameter()方法不能使用了。即使用了也获取的是null
2.使用request.getInputStream()来获取所有消息体数据。然后解析
解析消息体数据:
- 使用 commons-fileupload 组件解析消息体
步骤:
1.导入jar包。注意:还需要导入 commons-io.jar
2.创建解析器工厂
3.获取解析器
4.解析request对象,返回 List<FileItem> (表单项对象集合)
5.遍历集合,获取每一个表单项,获取数据
* FileItem对象的方法:
* 通用方法:
* 判断是否是普通表单项:isFormField()
* 获取表单项name名称:getFieldName()
* 普通表单项的方法:
* 获取表单项的值:getString(String encoding)
* 文件表单项的方法:
* 获取文件名称:getName()
* 获取文件类型:getContentType()
* 获取文件大小:getSize()
操作流程
* 获取文件关联的输出流:getOutputStream()
* 将文件数据写入硬盘:write(File file)
//解决文件名称乱码问题
request.setCharacterEncoding("utf-8");
//1.导入jar包
//2.创建解析器工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//3.获取解析器
ServletFileUpload fileUpload = new ServletFileUpload(factory);
//4.解析 request
List<FileItem> fileItems = fileUpload.parseRequest(request);
//5.遍历
for (FileItem fileItem : fileItems) {
//获取表单项的name属性值
String fieldName = fileItem.getFieldName();
System.out.println(fieldName);
//5.1判断
if(fileItem.isFormField()){
//普通表单项
//获取表单项提交的数据
//获取普通表单项的值
if (fieldName.equals("username")) {
String username = fileItem.getString("utf-8");
System.out.println("通表单项的值:" + username);
}
}else{
//文件表单项
//获取文件名称
String name = fileItem.getName();
//文件类型
String contentType = fileItem.getContentType();
//文件大小
long size = fileItem.getSize();
System.out.println(name);
System.out.println(contentType);
System.out.println(size);
//将文件写入硬盘上 这个upload文件夹一开始判断一下,如果不存在,要用代码创建出来
File dir = new File(this.getServletContext().getRealPath("/upload"));
String filename = UUIDUtils.randomUUID() + "_" + name;//随机文件名(保证文件名称唯一)
File file = new File(dir, filename);
fileItem.write(file);
}
System.out.println("-------------------------");
}
案例演示 JSP代码
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="http://localhost:8080/WenJianShangChuan_war_exploded/upload" method="post" enctype="multipart/form-data">
用户名 <input type="text" name="username" >
密码 <input type="password" name="password" >
选择上传的文件:<input type="file" name="uplod">
<input type="submit" value="提交">
</form>
</body>
</html>
Servlet代码
@WebServlet(name = "Upload", value = "/upload")
public class Upload extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload fileUpload = new ServletFileUpload(factory);
try {
List<FileItem> fileltems = fileUpload.parseRequest(request);
for (FileItem fileItem : fileltems) {
if (fileItem.isFormField()) {
if (fileItem.equals("username")) {
String username = fileItem.getString();
System.out.println("姓名"+username);
}
if (fileItem.equals("password")) {
String password = fileItem.getString();
System.out.println("密码"+password);
}
}else {
String name = fileItem.getName();
String contentType = fileItem.getContentType();
long size = fileItem.getSize();
File dir = new File(this.getServletContext().getRealPath("/upload"));
String filename=name;
File file = new File(dir, filename);
fileItem.write(file);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
成功后
3.2 文件下载
文件下载:服务器端保存到客户端
注意:下载时测试中文命名的文件 不要在eclipse 里面改中文名 可以在外面改好之后拿进来
* 下载方式:
1.超链接直接指向目标资源下载:
* 如果浏览器可以解析目标文件,则直接在浏览器中打开。反之,则弹出下载提示框
2.通过Servlet实现下载
1.超链接指向servlet,传递目标资源的标识符。
2.定义Servlet
2.1获取文件标识,找到文件对象
2.2创建输入流,关联目标文件
2.3设置响应头:
content-disposition:消息体数据的打开方式
* content-disposition:attachment;filename=xxx
content-type:类型
2.4将输入流数据写入response输出流中
下载细节:
文件名乱码:
1.Servlet获取文件名称,解决乱码,get请求
filename = new String(filename.getBytes("iso-8859-1"), "utf-8");
2.让下载提示框也显示中文。
2.1获取浏览器版本信息
String agent = request.getHeader("user-agent");
2.2根据不同的版本,发送不同的数据
<body>
<a href="/day13/xlj.avi">寻龙诀.avi 下载</a>
<a href="/day13/404.jpg">寻龙诀海报</a>
<hr>
<a href="/day13/downloadServlet?filename=寻龙诀.avi">寻龙诀.avi 下载</a>
<a href="/day13/downloadServlet?filename=404.jpg">寻龙诀海报</a>
</body>
// 1.获取文件名称
String filename = request.getParameter("filename");
//如是8.0以上版本的tomcat 这行不需要因为8.0的tomcat get请求 中文参数tomcat8.0已经处理了
//filename = new String(filename.getBytes("iso-8859-1"), "utf-8");
// 2.获取文件对象
String realPath = this.getServletContext().getRealPath("/" + filename);
// 3.创建输入流,关联文件
FileInputStream fis = new FileInputStream(realPath);
// 获取浏览器的版本信息,根据不同的浏览器,发送不同的数据(文件名称)
String agent = request.getHeader("user-agent");
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 if (agent.contains("Chrome")) {
// google浏览器
filename = URLEncoder.encode(filename, "utf-8");
} else {
// 其它浏览器
filename = URLEncoder.encode(filename, "utf-8");
}
// 4.设置响应头
// 告诉浏览器,响应消息体数据打开方式
response.setHeader("content-disposition", "attachment;filename="
+ filename);
response.setHeader("content-type", this.getServletContext()
.getMimeType(filename));
// 5.写数据
IOUtils.copy(fis, response.getOutputStream());
fis.close();
下载的常用API说明:
response.getOutputStream();
servletContext.getResourceAsStream();
servletContext.getMimeType();
response.setContentType();
response.setHeader(“Content-Disposition”,“attachment;fileName=1.jpg”);
这个响应头告诉浏览器。这是需要下载的。而attachment表示附件,也就是下载的一个文件**fileName=**后面,表示下载的文件名。
完成上面的两个步骤,下载文件是没问题了。但是如果我们要下载的文件是中文名的话。你会发现,下载无法正确显示出正确的中文名。
原因是在响应头中,不能包含有中文字符,只能包含ASCII码
URLEncoder解决IE和谷歌浏览器的附件中文问题
如果客户端浏览器是IE浏览器或者是谷歌浏览器。我们需要使用URLEncoder类先对中文名进行UTF-8的编码操作。
因为IE浏览器和谷歌浏览器收到含有编码后的字符串后会以UTF-8字符集进行解码显示。
//把中文名进行UTF-8编码操作。
Stringstr="attachment;fileName="+URLEncoder.encode("中文.jpg","UTF-8");
//然后把编码后的字符串设置到响应头中
response.setHeader("Content-Disposition",str);