WEB后端_Day04(文件上传、文件下载、EL表达式、JSTL标签库、页面JSP动态化、后端代码优化)
文件上传
前端:
- 表单的提交方式必须是post
- enctype=“multipart/form-data”
- 文件上传的input的type = file
后端:commonsFileupload commonsIO
步骤:
- 判断当前提交的表单是否是 multipart/form-data
- 获取ServletFileUpload
- 使用ServletFileUpload对象来解析请求
- 遍历解析而来的元素列表
- 判断每一个元素是普通表单元素还是文件上传
- 如果是普通表单 直接获取值即可 String str = item.getString();//表单元素的value属性值
- 如果是文件上传
获取文件名称
设置文件保存的目录
给文件设置一个新的名称 UUID.randomUUID().toString() +文件的本身的扩展名
使用fileItem.write(文件保存的目录) fileItem是文件上传项
文件下载
页面前端:
待下载文件的列表(使用超链接a标签)
思路:当我们点击超链接的时候 提交一个请求到服务端的一个servlet处理我们下载的请求,在请求中 我们需要告诉服务器 需要下载的文件的名称(或者id)。
id | path | uuidname | realName | exp | createtime |
---|---|---|---|---|---|
1 | 20210402 | f68d9aaa-1f45-43b2-928b-622817b7972a | ab | .jpg | 2021-04-03 15:23 |
后端:
- 获取前端传递过来的要下载的文件的id
- 通过id查询要下载的文件的详细信息
- 组合文件在服务器上的对象
- 设置文件下载的属性response.setHeader(“Content-Disposition”, “attachment; fileName=1.jpg”);
- 创建一个文件的输入流
- 使用流将文件保存到一个位置
<a href="download?path=20210402/8aadd81a-81d6-42e4-96d6-51e0dcb68cb4.doc">待下载文件</a>
servlet
package cn.lanqiao.controller;
import org.apache.commons.io.IOUtils;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*
public class DownloadServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取要下载文件的路径
String path = req.getParameter("path");
//设置响应头 作为附件附件
resp.setHeader("Content-Disposition", "attachment; fileName=课堂笔记.doc");
//获取文件的minetype
ServletContext context = getServletContext();
String minetype = context.getMimeType("e://upload/"+path);
//设置响应的内容类型
resp.setContentType(minetype);
//获取响应的输出流
OutputStream os = resp.getOutputStream();
//获取要下载的文件的输入流
InputStream is = new FileInputStream(new File("e://upload/"+path));
//实现文件的下载
IOUtils.copy(is,os);
}
}
解决下载过程中文件的名称为中文时的中文乱码问题
//解决中文乱码 对Content-Disposition内容进行编码 浏览器会自动解析URLEncoder编码的字符串
String contentValue = "attachment; fileName="+URLEncoder.encode("课堂笔记.doc","UTF-8");
resp.setHeader("Content-Disposition", contentValue);
EL 表达式
jsp脚本的问题:
-
格式嵌套 不适合于复杂的嵌套
-
html代码和java代码混合在一起 使得程序不易于维护
什么是EL 表达式,EL 表达式的作用
EL 表达式的全称是:Expression Language。是表达式语言。
EL 表达式的什么作用:EL 表达式主要是代替jsp 页面中的表达式脚本在jsp 页面中进行数据的输出。因为EL 表达式在输出数据的时候,要比jsp 的表达式脚本要简洁很多
<h1>jsp脚本取值</h1>
<%=request.getAttribute("req")%><br/><br/>
<%=request.getAttribute("req1")%><br/><br/>
<%=request.getAttribute("req2")%><br/><br/>
<h1>EL表达式</h1>
${requestScope.req}<br/><br/>
${req1}<br/><br/>
${requestScope.req2}
EL 表达式的格式是:${表达式}
EL 表达式在输出null 值的时候,输出的是空串。jsp 表达式脚本输出null 值的时候,输出的是null 字符串
EL 表达式搜索域数据的顺序
EL 表达式主要是在jsp 页面中输出数据。主要是输出域对象中的数据。
当四个域中都有相同的key 的数据的时候,EL 表达式会按照四个域的从小到大的顺序去进行搜索,找到就输出。
req.setAttribute("key","value1");
req.getSession().setAttribute("key","value2");
req.getServletContext().setAttribute("key","value3");
${key}
表达式输出Bean 的普通属性,数组属性。List 集合属性,map 集合属性
- 需求——输出Person 类中普通属性,数组属性。list 集合属性和map 集合属性。
public class Person {
private String name;
private String[] phones;
private List<String> citys;
private Map<String, Object> hoby;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Person person = new Person();
person.setName("张三");
person.setPhones(new String[]{"188888888","19999999","1555555555"});
List<String> citys = new ArrayList<>();
citys.add("太原");
citys.add("运城");
citys.add("临汾");
person.setCitys(citys);
Map<String, Object> hobys = new HashMap<>();
hobys.put("basketball","篮球");
hobys.put("footb","足球");
person.setHoby(hobys);
req.setAttribute("person",person);
req.getRequestDispatcher("/index.jsp").forward(req,resp);
}
<body>
<h1>获取对象的属性</h1>
姓名:${person.name}<br/><br/>
电话:${person.phones[0]}<br/><br/>
城市:${person.citys[1]}<br/><br/>
爱好:${person.hoby.basketball}
</body>
获取数组和集合及map的所有元素
<h1>获取对象的属性</h1>
姓名:${person.name}<br/><br/>
电话:${person.phones}<br/><br/>
城市:${person.citys}<br/><br/>
爱好:${person.hoby}
表达式——运算
语法:${ 运算表达式} , EL 表达式支持如下运算符:
- 关系运算
关系运算符 | 说 明 | 范 例 | 结果 |
---|---|---|---|
== 或 eq | 等于 | ${ 5 == 5 } 或 ${ 5 eq 5 } | true |
!= 或 ne | 不等于 | ${ 5 !=5 } 或 ${ 5 ne 5 } | false |
< 或 lt | 小于 | ${ 3 < 5 } 或 ${ 3 lt 5 } | true |
> 或 gt | 大于 | ${ 2 > 10 } 或 ${ 2 gt 10 } | false |
<= 或 le | 小于等于 | ${ 5 <= 12 } 或 ${ 5 le 12 } | true |
>= 或 ge | 大于等于 | ${ 3 >= 5 } 或 ${ 3 ge 5 } | false |
- 逻辑运算
逻辑运算符 | 说 明 | 范 例 | 结果 |
---|---|---|---|
&& 或 and | 与运算 | ${ 12 == 12 && 12 < 11 } 或 ${ 12 == 12 and 12 < 11 } | false |
|| 或 or | 或运算 | ${ 12 == 12 || 12 < 11 } 或 ${ 12 == 12 or 12 < 11 } | true |
! 或 not | 取反运算 | ${ !true } 或 ${not true } | false |
- 算数运算
算数运算符 | 说 明 | 范 例 | 结果 |
---|---|---|---|
+ | 加法 | ${ 12 + 18 } | 30 |
- | 减法 | ${ 18 - 8 } | 10 |
* | 乘法 | ${ 12 * 12 } | 144 |
/ 或 div | 除法 | 144 / 12 或 { 144 / 12 }或 144/12或{ 144 div 12 } | 12 |
% 或 mod | 取模 | KaTeX parse error: Expected '}', got 'EOF' at end of input: { 144 % 10 }或{ 144 mod 10 } | 4 |
- empty 运算
empty 运算可以判断一个数据是否为空,如果为空,则输出true,不为空输出false。
以下几种情况为空:
-
值为null 值的时候,为空
-
值为空串的时候,为空
-
值是Object 类型数组,长度为零的时候
-
list 集合,元素个数为零
-
map 集合,元素个数为零
<%
String str ="";
Object obj = null;
List list= new ArrayList();
Map map = new HashMap();
Object[] arrObj = new Object[]{};
int[] arrInt= new int[]{};
request.setAttribute("str",str);
request.setAttribute("obj",obj);
request.setAttribute("list",list);
request.setAttribute("map",map);
request.setAttribute("arrObj",arrObj);
request.setAttribute("arrInt",arrInt);
%>
<%-- true--%>
${empty str}<br/>
<%-- true--%>
${empty obj}<br/>
<%-- true--%>
${empty list}<br/>
<%-- true--%>
${empty map}<br/>
<%-- false--%>
${empty arrInt} <br/>
<%-- true--%>
${empty arrObj} <br/>
</body>
- 三元运算
表达式1?表达式2:表达式3
如果表达式1 的值为真,返回表达式2 的值,如果表达式1 的值为假,返回表达式3 的值。
${5==5?"true":"false"}
- “.”点运算和[] 中括号运算符
.点运算,可以输出Bean 对象中某个属性的值。
[]中括号运算,可以输出有序集合中某个元素的值。
并且[]中括号运算,还可以输出map 集合中key 里含有特殊字符的key 的值。
爱好:${person.hoby['basketball']}//此时的key必须加上单引号
表达式的11 个隐含对象
EL 个达式中11 个隐含对象,是EL 表达式中自己定义的,可以直接使用。
隐含对象 | 类型 | 说明 |
---|---|---|
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 中配置的上下文参数 |
http://localhost:8080/index.jsp?username=admin
${param.username}
pageContext 对象的使用
-
协议:
-
服务器ip:
-
服务器端口:
-
获取工程路径:
-
获取请求方法:
-
获取客户端ip 地址:
-
获取会话的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>
<hr/>
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>
请求地址:
http://localhost:8080/09_EL_JSTL/other_el_obj.jsp?username=wzg168&password=666666&hobby=java&hobby=cpp
header Map<String,String> 它可以获取请求头的信息
header Values Map<String,String[]> 它可以获取请求头的信息,它可以获取多个值的情况
示例代码:
输出请求头【User-Agent】的值:
h
e
a
d
e
r
[
′
U
s
e
r
−
A
g
e
n
t
′
]
<
b
r
>
输
出
请
求
头
【
C
o
n
n
e
c
t
i
o
n
】
的
值
:
{ header['User-Agent'] } <br> 输出请求头【Connection】的值:
header[′User−Agent′]<br>输出请求头【Connection】的值:{ header.Connection }
输出请求头【User-Agent】的值:${ headerValues[‘User-Agent’][0] }
cookie Map<String,Cookie> 它可以获取当前请求的Cookie 信息
示例代码:
获取Cookie 的名称:${ cookie.JSESSIONID.name } <br>
获取Cookie 的值:${ cookie.JSESSIONID.value } <br>
initParam Map<String,String> 它可以获取在web.xml 中配置的上下文参数
web.xml 中的配置:
<context-param>
<param-name>username</param-name>
<param-value>root</param-value>
</context-param>
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql:///test</param-value>
</context-param>
示例代码:
输出<Context-param>username 的值:${ initParam.username } <br>
输出<Context-param>url 的值:${ initParam.url } <br>
JSTL 标签库(重点)
JSTL 标签库全称是指JSP Standard Tag Library JSP 标准标签库。是一个不断完善的开放源代码的JSP 标签库。
EL 表达式主要是为了替换jsp 中的表达式脚本,而标签库则是为了替换代码脚本。这样使得整个jsp 页面变得更佳简洁。
JSTL 由五个不同功能的标签库组成
功能范围 | URI | 前缀 |
---|---|---|
核心标签库–重点 | http://java.sun.com/jsp/jstl/core | c |
格式化 | http://java.sun.com/jsp/jstl/fmt | fmt |
函数 | http://java.sun.com/jsp/jstl/functions | fn |
数据库(不使用) | http://java.sun.com/jsp/jstl/sql | sql |
XML(不使用) | http://java.sun.com/jsp/jstl/xml | x |
使用
导包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WUvOu8Qd-1617617768848)(assets/image-20210403141607314.png)]
引入所要使用的标签库
<%-- 引入所要使用的标签库--%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--格式化库--%>
<%@taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%--函数库--%>
<%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
core 核心库使用
<c:set />(使用很少)
<%-- 作用:set 标签可以往域中保存数据
域对象.setAttribute(key,value);
scope 属性设置保存到哪个域
page 表示PageContext 域(默认值)
request 表示Request 域
session 表示Session 域
application 表示ServletContext 域
var 属性设置key 是多少
value 属性设置值
--%>
<c:set scope="request" value="admin" var="user" />
${user}
<c:if />
<c:set var="sex" value="0" scope="request"/>
<c:if test="${sex == 1}">
<h4>男</h4>
</c:if>
<c:if test="${sex == 0}">
<h4>女</h4>
</c:if>
注意:没有c:else
<c:choose> <c:when> <c:otherwise>标签
<c:set var="age" scope="request" value="2"/>
<c:choose>
<c:when test="${age > 18 && age < 30}">
成年人
</c:when>
<c:when test="${age > 30}">
中年人
</c:when>
<c:otherwise>
<c:choose>
<c:when test="${age > 0 && age<3}">
婴幼儿
</c:when>
</c:choose>
</c:otherwise>
</c:choose>
<c:forEach />
<c:forEach var="i" begin="0" end="10" step="2">
<h5>${i}</h5>
</c:forEach>
<c:forEach var="i" begin="0" end="10" step="2">
<h5>${i}</h5>
</c:forEach>
<%--遍历集合list items就是需要遍历的集合对象或集合属性--%>
<c:forEach items="${person.citys}" var="city">
<h5>${city}</h5>
</c:forEach>
<%-- 遍历集合Map--%>
<c:forEach items="${person.hoby}" var="entity">
<h5>${entity.key}---${entity.value}</h5>
</c:forEach>
页面jsp 动态化(零点读书)
将页面的扩展名改为.jsp
同时需要为页面添加page指令
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%;
String basePath = request.getScheme()+"://" + request.getServerName()+":"+request.getServerPort()+request.getContextPath()+"/";
%>
<base href="<%=basePath%>">
将页面的公共部分 单独列出来
<jsp:include page="/pages/public/foot.jsp"/>
<jsp:include page="/pages/public/top.jsp"/>
登录,注册错误提示,及表单回显
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3wq55sgR-1617617768852)(assets/image-20210403155044290.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TZxNKw95-1617617768855)(assets/image-20210403155115440.png)]
后端代码的优化
一个servlet处理多个请求
页面使用隐藏域来传递请求的真正的方法
<input type="hidden" name="_method" value="login">
public class UserServlet extends HttpServlet {
private IUserService userService = new UserServiceImpl();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("_method");
if("login".equals(method)){
login(req,resp);
}else if("regist".equals(method)){
regist(req,resp);
}
}
public void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 接收用户输入的用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = userService.login(username,password);
System.out.println("user = " + user);
if(user!=null){
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);
}else{
//當用戶輸入的 用戶名密碼錯誤時 需要給出明確的提示
req.setAttribute("errorMsg","用戶名或密碼錯誤");
req.setAttribute("username",username);
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
}
}
public void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username= req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
User user = new User(username,password,email);
userService.save(user);
req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req,resp);
}
}
在以上的代码中 由于可能会存在大量的if…else 为了减少判断 再此使用反射来优化代码
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("_method");
try {
Method method1= this.getClass().getDeclaredMethod(method,HttpServletRequest.class,HttpServletResponse.class);
method1.invoke(this,req,resp);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
为了减少代码的冗余 将上述代码抽取出来 单独形成一个baseServlet
public abstract class BaseServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getParameter("_method");
try {
Method method1= this.getClass().getDeclaredMethod(method,HttpServletRequest.class,HttpServletResponse.class);
method1.invoke(this,req,resp);
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
此时我们的自定以servlet只需要去继承BaseServlet
public class UserServlet extends BaseServlet {
private IUserService userService = new UserServiceImpl();
public void login(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 接收用户输入的用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
User user = userService.login(username,password);
System.out.println("user = " + user);
if(user!=null){
req.getRequestDispatcher("/pages/user/login_success.jsp").forward(req,resp);
}else{
//当用户输入的用户名或密码错误时,需要给出明确地提示
req.setAttribute("errorMsg","用戶名或密码错误");
req.setAttribute("username",username);
req.getRequestDispatcher("/pages/user/login.jsp").forward(req,resp);
}
}
public void regist(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username= req.getParameter("username");
String password = req.getParameter("password");
String email = req.getParameter("email");
User user = new User(username,password,email);
userService.save(user);
req.getRequestDispatcher("/pages/user/regist_success.jsp").forward(req,resp);
}
}