接受请求
获得请求参数
业务处理( 不直接做 ) 外包Servlet
响应浏览器( 打标签 ) 外包给JSP
JSP(全称JavaServer Pages)是由Sun Microsystems公司主导创建的一种动态网页技术标准。JSP部署于网络服务器上,可以响应客户端发送的请求,并根据请求内容动态地生成HTML、XML或其他格式文档的Web网页,然后返回给请求者。JSP技术以Java语言作为脚本语言,为用户的HTTP请求提供服务,并能与服务器上的其它Java程序共同处理复杂的业务需求。
使用Servlet,实现动态网页,那么需要编写大量的输出语句,手工拼接网页内容,相当恶心。
JSP将Java代码和特定变动内容嵌入到静态的页面中,实现以静态页面为模板,动态生成其中的部分内容。JSP引入了被称为“JSP动作”的XML标签,用来调用内建功能。另外,可以创建JSP标签库,然后像使用标准HTML或XML标签一样使用它们。标签库能增强功能和服务器性能,而且不受跨平台问题的限制。
JSP文件在运行时会被其编译器转换成更原始的Servlet**代码。**JSP编译器可以把JSP文件编译成用Java代码写的Servlet,然后再由Java编译器来编译成能快速执行的二进制机器码,也可以直接编译成二进制码。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>第一个JSP页面title>
head>
<body>
Hello JSP
body>
html>
JSP 文件后缀 .jsp jsp和html没太大区别,多了一些jsp专属的成分。
<%-- 服务器注释脚本 --%>
服务器注释脚本 源代码看不见 html 注释前端源代码可见
<%
for(int i=1;i<=9;i++){
for(int j=1;j<=i;j++){
out.println( i+"*"+j+"="+(i*j));
}
out.println("<br/>");
}
%>
方便在jsp页面中嵌入 程序逻辑代码
<%!
String bookName="葵花宝典";
public String getBook(){
return bookName;
}
%>
用来定义变量或者方法
<div style="color: red">
<%=bookName%>
div>
<div style="color: red">
<%=getBook()%>
div>
输出变量值,或者函数调用的返回值
jsp 本质上就是一个Servlet,但是编写jsp比编写servlet要简单的多,尤其是展现页面的时候,jsp可以直接在页面中编写标签,而Servlet需要通过输出语句拼接。
用户编写jsp代码最终会被编译成一个Java类(类似于Servlet),你认为很蠢的操作,其实有人帮你做。本质和Servlet拼接原理一致,使用JSP表面写标签,背地里转换成输出语句动态输出这些标签代码。
package org.apache.jsp;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
public final class life_jsp extends org.apache.jasper.runtime.HttpJspBase
implements org.apache.jasper.runtime.JspSourceDependent,
org.apache.jasper.runtime.JspSourceImports {
public void _jspInit() {
}
public void _jspDestroy() {
}
public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)
throws java.io.IOException, javax.servlet.ServletException {
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write("\r\n");
out.write(" Title \r\n");
out.write("\r\n");
out.write("\r\n");
out.write(" 测试原理和生命周期2
\r\n");
out.write("\r\n");
out.write("\r\n");
}
}
生命周期和Servlet一致,多了一个环节(编译环节,jsp转换成java代码),第一次请求或者当jsp代码发生改变时,进行编译转换。
总结: Servlet 和jsp 都是动态网页技术,jsp底层实现就以servlet方式实现的,本质是一样的,只是jsp在展示页面方面要强于servlet。
指令是jsp特有的语法,指令可以增强页面表达的功能,不同指令有不同的含义,在jsp中存在3大指令元素。
指令的语法
<%@ commandName attr %>
<%@ _page _
contentType=“text/html;charset=UTF-8” // 指定响应页面时的MIME resp.setContentType()
language=“java” // 声明页面中的脚本语言为java
import=" import=“com.qfedu.entity.Girl” // 导包
pageEncoding=“utf-8” // 指定页面的编码类型 resp.setCharacterEncoding(“utf-8”)
isErrorPage=“true|false”
errorPage=“” //指定错误页面
%>
指定页面的一些配置信息
<%@ include file=“被包含文件的路径” %>
指令包含,会把被包含的源代码与自己的代码合成。
在jsp中除了 html提供的标签以外,官方还提供了其他的标签,这些标签与静态标签不同 ,它们具备逻辑处理能力。比如判断,循环,格式化,等等。所以这个标签主要是引入其他第三方的标签库。
JSTL:(Java server pages standarded tag library) (jsp的标准标签库,是一个基础的标签库)
1. 导入jar
2. 引入
<%@ taglib uri=“http://java.sun.com/jsp/jstl/core” prefix=“c” %>
3. 使用
这里只是会引入即可,后续学习标签库中的内容,jstl 要结合 el表达式才能发挥出功力。
jsp 动作可以增强页面行为功能的一组标签,目的为了简化脚本(消灭脚本),原本我们认为在jsp中可以编写java脚本代码,可以很方便的实现动态功能。在jsp 中引入动作可以简化一些脚本代码,使页面内容相对统一,方便维护和升级。
什么是JavaBean :
JAVAEE – java企业级版本(企业级应用技术栈 ) JavaBean(描述的就是一个类(泛指),这个类 具备get set 方法(狭义))
创建一个JavaBean , 简单的说就是java对象 id 对象名称 class 对象类类型 ,底层使用反射机制创建对象。
为对象属性赋值 name 对象 property 对象的哪个属性 value 属性值
取对象属性值 name 对象 property 对象的哪个属性
等价于servlet中的转发
转发时候 携带参数
org.apache.jasper.runtime.JspRuntimeLibrary.include(request, response, “simple.jsp”, out, false); 使用时去读去被包含页面的内容。
内置对象就是可以在jsp中直接使用的对象,如何理解内置对象呢?这些对象其实是已经声明好了的。因为我们编写的JSP最终会被转换成Servlet代码,那么我们所谓的内置对象其实就是将来Servlet里面的声明好的对象,只是提前使用而已。
这些内置对象几乎就是Servlet时期使用过类型对象,那么他们的使用方法,都掌握了。
<%-- 取数据 --%>
<%
String data = request.getParameter("data");
%>
<%=data%>
<%--转发--%>
<%
request.getRequestDispatcher("/index.jsp").forward(request,response);
%>
以前怎么用现在还是怎么用。
<%
response.sendRedirect("http://www.baidu.com");
%>
以前怎么用现在还是怎么用。
<%@ page contentType="text/html;charset=UTF-8" language="java" isErrorPage="true" %>
<html>
<head>
<title>Titletitle>
head>
<body bgcolor="red">
<h1>服务器内部异常,服务器离家出走了.......h1>
<%
if( exception instanceof NullPointerException ){
//空指针
response.sendRedirect("http://www.sina.com");
}
if(exception instanceof ArithmeticException){
//算术异常
response.sendRedirect("http://www.baidu.com");
}
%>
body>
html>
isErrorPage=“true” 标记一个页面为错误页面
<%@ page contentType="text/html;charset=UTF-8" language="java" errorPage="server_error.jsp" %>
<html>
<head>
<title>$Title$title>
head>
<body>
<%
// 模拟错误
out.println("Hello world");
int i = 1/0;
%>
body>
html>
errorPage=“server_error.jsp”
在jsp中存在4个作用域其中3个是Servlet阶段学习过的(application session request page ) ,域的作用是为了传递数据,在开发中我们可以利用Servlet 处理请求 利用JSP渲染数据。而作用域起到的就是两者间的数据传递问题。
需要注意的是 跳转方式不同,对域的选择就不同,比如重定向就不可以使用request 域。
Servlet 接受请求-获得数据-跳转
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得数据(可以从数据库查)
User user1 = new User("王震",80);
User user2 = new User("刘杰",90);
User user3 = new User("天宇",100);
//把数据存入域中
req.setAttribute("req",user1);
req.getSession().setAttribute("ses",user2);
this.getServletContext().setAttribute("app",user3);
//跳转到页面渲染(jsp 做跟方便)
//req.getRequestDispatcher("/show.jsp").forward(req,resp);
//resp.sendRedirect("/show.jsp");
}
JSP 从域中获得数据-渲染页面
req:
<%=request.getAttribute("req") %>
session:
<%=session.getAttribute("ses") %>
app:
<%=application.getAttribute("app") %>
理解 pageContext 页面上下文 -> 页面全局-> 可以管理页面上的全部作用域。
pageContext.setAttribute("xx","罗志祥"); // 默认存入page域
pageContext.setAttribute("nb","张柏芝",PageContext.SESSION_SCOPE);//把数据存入指定域
pageContext.getAttribute("xx") //默认从page域中获取数据
pageContext.getAttribute("req", PageContext.REQUEST_SCOPE ) // 从指定域中获得数据
特定域中获取数据可设置数据,通过 几个常量表示域类型:
PageContext.PAGE_SCOPE
PageContext.REQUEST_SCOPE
PageContext.SESSION_SCOPE
PageContext.APPLICATION_SCOPE
<%--pageContext搜索用法 : --%>
page:
<%= pageContext.findAttribute("xx") %>
req:
<%= pageContext.findAttribute("req") %>
session:
<%= pageContext.findAttribute("ses") %>
app:
<%= pageContext.findAttribute("app") %>
只需要给key 他会依次从page request session application 搜索,找到则立即返回
EL(Expression Language) 是为了使JSP写起来更加简单。表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法,让Jsp的代码更加简化。虽然通过jsp的内置对象,可以很容易管理各种域,但是始终会出现脚本,使用EL结合JSTL标签库就可以彻底消灭脚本。
**${ 表达式|key } **
整型 浮点型 字符串 对象 布尔 NULL
${123} => 123
${3.14} => 3.14
${ empty 对象 }
如果对象为空则返回true 否则false
<%=request.getParameter("nb")%>
等价
${param.nb}
这些el内置目前没有实际应用机会,这些都应该交给Servlet做。
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$title>
head>
<body>
<%
pageContext.setAttribute("xx","小红");
%>
<%--使用EL表达式取值--%>
page: ${pageScope.xx}
<hr>
req: ${requestScope.req}
<hr>
ses: ${sessionScope.ses}
<hr>
app: ${applicationScope.app}
<%--使用EL表达式的搜索方式取值--%>
<hr color="red">
page: ${xx}
<hr>
req: ${req}
<hr>
ses: ${ses}
<hr>
app: ${app}
body>
html>
推荐直接使用 key 搜索。
<%-- 不同于jsp内置对象 --%>
${pageContext.session.id}
它的作用是 获得jsp的内置对象,可以容易使用它们的api
到这里重点是使用EL从域中取值${key}
仅仅使用EL还不足以完全消灭脚本,EL表达式需要配合标签才能发挥出最大开发效率。EL+JSTL 消除脚本。
https://mvnrepository.com/artifact/javax.servlet/jstl/1.2
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%--
items="" 指定遍历的集合
var="" 遍历出的每个元素
--%>
<c:forEach items="${list}" var="u" >
<tr>
<td>${u.name}td>
<td>${u.age}td>
tr>
c:forEach>
遍历集合功能 , 需要给对象get方法。
<%
int level=3;
pageContext.setAttribute("grade",level);
%>
<c:if test="${grade<=5 and grade>=1}">
<h6>倔强青铜h6>
c:if>
<c:if test="${grade<=10 and grade>=6}">
<h6>最强王者h6>
c:if>
<%
int level=3;
pageContext.setAttribute("grade",level);
%>
<c:choose>
<c:when test="${grade==1}">
倔强青铜
c:when>
<c:when test="${grade==2}">
不屈清透
c:when>
<c:when test="${grade==3}">
永恒钻石
c:when>
<c:when test="${grade==4}">
最强王者
c:when>
<c:otherwise>
垃圾电脑
c:otherwise>
c:choose>
<%
Date birthday = new Date();
pageContext.setAttribute("birthday",birthday);
%>
<span> <f:formatDate value="${birthday}" pattern="dd/MM/yyyy" /> span>
MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。
MVC 设计就是分层设计,__解耦。
和3层架构(表示层 业务逻辑层 数据持久层)的关系?它们本身是不同的两种软件分层设计思想。在实际开发中,都使用了,宏观上使用MVC, 细化后也引入了三层架构。
JSP+**JavaBean **开发模式(直接在JSP中使用JAVABean) ,不符合MVC思想。js
**Servlet **+ **JavaBean **+ **JSP **开发模式,已经符合MVC模式
Servlet 作为控制器 Controller
JSP 作为视图 View
JavaBean 模型 Model
使用第三方 开发的软件产品 比如 Struts2 (淘汰) SpringMVC(主流) 它们是MVC实现的具体方案。
SSH2:(Spring+Struts2+Hibernate) ssh SSM: (Spring + SpringMVC+ MyBatis)
当用户请求后台数据时,由于数据量非常大,不可能全部一次性给到前端,可以使用分页功能,把数据分块,给用户更好的使用体验。
/**
* 分页工具类,描述一页的数据
*/
public class Pager<T> {
//数据
private List<T> list;
//页码
private Integer page;
//大小
private Integer size;
//总记录数
private Integer total;
//总页数
private Integer pages;
//是否有上一下
private boolean hasNext;
//是否有上一页
private boolean hasPrev;
//指示按钮
private int[] buttons;
public Pager() { }
public Pager(List<T> list, Integer page, Integer size, Integer total) {
this.list = list;
this.page = page;
this.size = size;
this.total = total;
//计算总页数
this.pages = total % size == 0 ? total / size : total / size + 1;
}
public List<T> getList() {
return list;
}
public Integer getPage() {
return page;
}
public Integer getSize() {
return size;
}
public Integer getTotal() {
return total;
}
public Integer getPages() {
return pages;
}
public boolean isHasNext() {
return !page.equals(pages);
}
public boolean isHasPrev() {
return !page.equals(1);
}
public int[] getButtons() {
//生成按钮数字 Stream
if( pages < 10 ){
buttons = IntStream.rangeClosed(1,page).toArray();
}else{
int start = page%10==0? ( (page-1)/10)*10 + 1 : (page/10)*10 + 1;
int end = start+9;
if(end>pages){
end = pages;
}
buttons = IntStream.rangeClosed(start,end).toArray();
}
return buttons;
}
}
public Pager<Singer> findByPageWithCondition(int page, int size, Singer singer) throws Exception {
//数据集合
List<Object> params = new ArrayList<>();
//查询数据SQL
StringBuilder sqlData = new StringBuilder( " select * from tb_singer " );
//查询总记录数SQL
StringBuilder sqlCount = new StringBuilder( " select count(*) from tb_singer " );
//片段
StringBuilder whereSQL = new StringBuilder(" where 1=1 " );
StringBuilder limitSQL = new StringBuilder( " limit ? ,? " );
if( singer.getSno()!=null && singer.getSno()!=0 ){
whereSQL.append( " and sno = ? " );
params.add( singer.getSno() );
}
if( singer.getSname()!=null && !"".equals(singer.getSname()) ){
whereSQL.append( " and sname = ? " );
params.add( singer.getSname() );
}
if( singer.getFen()!=null && singer.getFen()!=0 ){
whereSQL.append( " and fen >= ? " );
params.add( singer.getFen() );
}
//limit 从句的数据
params.add( (page-1)*size );
params.add( size );
//拼接好的查询数据SQL 和 统计SQL
String queryDataSql = sqlData.append(whereSQL).append(limitSQL).toString();
String queryCountSql = sqlCount.append(whereSQL).toString();
//分别执行SQL
List<Singer> list = queryRunner.query(queryDataSql, new BeanListHandler<>(Singer.class), params.toArray());
Long count = queryRunner.query(queryCountSql, new ScalarHandler<Long>(), params.subList(0, params.size() - 2).toArray());
//构造查询结果
Pager<Singer> pager = new Pager<>(list,page,size, count.intValue());
//返回
return pager;
}
package com.qfedu.mvc.controller;
import com.qfedu.mvc.entity.Singer;
import com.qfedu.mvc.service.SingerService;
import com.qfedu.mvc.service.impl.SingerServiceImpl;
import com.qfedu.mvc.util.Pager;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@WebServlet("/singer/list2.do")
public class SingerQueryPlusController 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 page = req.getParameter("page");
String size = req.getParameter("size");
if( page==null || "".equals(page) ){
page = "1";
}
if( size==null || "".equals(size) ){
size = "10";
}
int pageNum = Integer.parseInt(page);
int pageSize = Integer.parseInt(size);
//用户过虑条件数据
String sno = req.getParameter("sno");
String sname = req.getParameter("sname");
String fen = req.getParameter("fen");
//封装查询条件对象
Singer singer = new Singer();
singer.setSno( sno==null||"".equals(sno)?null:Integer.parseInt(sno));
singer.setFen( fen==null||"".equals(fen)?null:Integer.parseInt(fen));
singer.setSname(sname);
//数据回现
req.setAttribute("cd",singer);
//调用业务逻辑
SingerService singerService = new SingerServiceImpl();
try {
Pager<Singer> pager = singerService.findByPageWithCondition(pageNum,pageSize,singer);
//存在数据
req.setAttribute("pager",pager);
//跳转到JSP
req.getRequestDispatcher("/singers.jsp").forward(req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>Title</title>
<link href="/css/bootstrap.css" rel="stylesheet">
</head>
<body>
<%--条件部分--%>
<div style="width: 800px;margin: 10px auto">
<form action="/singer/list2.do" method="post" class="form-inline" >
ID
<input name="sno" type="text" value="${cd.sno}" placeholder="编号" class="form-control form-control-sm">
Name
<input name="sname" type="text" value="${cd.sname}" placeholder="姓名" class="form-control form-control-sm" >
Fen
<select name="fen" class="form-control form-control-sm" >
<option value="0" ${cd.fen==0?'selected':'' } >全部</option>
<option value="10" ${cd.fen==10?'selected':'' } >10</option>
<option value="50" ${cd.fen==50?'selected':'' } >50</option>
<option value="100" ${cd.fen==100?'selected':'' } >100</option>
</select>
<input type="submit" value="查询" class="btn btn-primary btn-sm" >
</form>
</div>
<%--数据显示部分--%>
<table class="table table-sm " style="width: 800px;margin: 0 auto">
<thead class="table-dark">
<tr>
<td>编号</td>
<td>名字</td>
<td>信息</td>
<td>粉丝</td>
<td>头像</td>
<td>排名</td>
<td>操作</td>
</tr>
</thead>
<c:forEach items="${pager.list}" var="s">
<tr>
<td>${s.sno}</td>
<td>${s.sname}</td>
<td>${s.info}</td>
<td>${s.fen}</td>
<td><img src="${s.photo}" style="width: 30px;height: 30px"></td>
<td>${s.ranks}</td>
<td>
<a href="#" class="btn btn-sm btn-link">删除</a>
<a href="#" class="btn btn-sm btn-link">修改</a>
</td>
</tr>
</c:forEach>
</table>
<%--分页按钮部分--%>
<div style="width: 800px;margin: 0 auto; text-align: center">
<nav aria-label="Page navigation example">
<ul class="pagination pagination-sm">
<%--首页--%>
<li class="page-item"><a class="page-link" href="/singer/list.do?page=1">首页</a></li>
<%--上一页--%>
<c:if test="${pager.hasPrev}">
<li class="page-item"><a class="page-link" href="/singer/list.do?page=${pager.page-1}">上一页</a></li>
</c:if>
<%--页码--%>
<c:forEach items="${pager.buttons}" var="num">
<li class="page-item ${pager.page==num?'active':'' }" ><a class="page-link" href="/singer/list.do?page=${num}">${num}</a></li>
</c:forEach>
<%--下一页--%>
<c:if test="${pager.hasNext}">
<a></a>
<li class="page-item"><a class="page-link" href="/singer/list.do?page=${pager.page+1}" >下一页</a></li>
</c:if>
<%--尾页--%>
<li class="page-item"><a class="page-link" href="/singer/list.do?page=${pager.pages}">尾页</a></li>
</ul>
</nav>
当前<span>${pager.page}</span>/<span>${pager.pages}</span>页,共<span>${pager.total}条记录</span>
</div>
</body>
</html>
文件上传下载是网站中十分常见的功能,比如上传头像,下载文件模板,这些功能都是网站的基础服务功能, JavaWeb中文件上传,通常是基于Servlet来接收请求并保存文件 ,上传页面可以是html 或者 jsp 都可以。基本流程如下:
扩展 对应文件保存路径,不一定保存在一个文件夹下,可以采用一些算法来拆分,比如,以日期为区分,不同日期的在一个文件夹。或者通过hashcode散列方式, 把文件保存到不同文件夹中。
文件上传三要素
<form action="/upload.do" method="post" enctype="multipart/form-data">
<input name="username" type="text">
<input name="myfile1" type="file">
<input name="myfile2" type="file">
<input type="submit" value="上传">
form>
接收控制器使用Serlvet ,但是在Servlet 3.0以前 ,对应文件上传支持不友好,通常使用 commons-fileupload-1.4.jar commons-io-2.5.jar 实现文件上传。
引入相关jar包后,控制器编写如下;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//第三方文件上上传库jar (Apache-commons-fileUpload)
//接受文件 Servlet3.1 自带上功能
//验证当前的请求是否为一个包含文件上传的请求
if( !ServletFileUpload.isMultipartContent(req) ){
resp.getWriter().println("enctype使用multipart/form-data");
return;
}
//确定保存位置
String savePath = this.getServletContext().getRealPath("/upload");
System.out.println(savePath);
File saveDir = new File(savePath);
//保存路径对应的文件夹不存在,则创建
if(!saveDir.exists()){
saveDir.mkdirs();
}
//文件上传工厂
DiskFileItemFactory factory = new DiskFileItemFactory();
//文件上传处理器
ServletFileUpload upload = new ServletFileUpload(factory);
//解析上传请求
try {
List<FileItem> list = upload.parseRequest(req);
for (FileItem item:list){
//区分类型
if( item.isFormField() ){ //普通的表单字段
// 接受普通字段
System.out.println(item.getString());
}else{
//非表单字段即是文件(保存文件)
item.write( new File( savePath+"/"+item.getName() ) );
}
}
} catch (FileUploadException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
从Servlet3.0 开始 Servlet 对文件上传 实现了直接支持,不需要引入其他jar。 通过一个注解就可以方便实现上传。通过getPart( String name ) 直接可以获得表单中的文件对象。 如果是多文件上传后端使用 getParts( String name )。
maxFileSize 单个文件最大 大小
maxRequestSize 整个表单总大小
@MultipartConfig
@WebServlet("/upload.do")
public class UploadController extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//接收文件
Part photo = req.getPart("photo");
//保存这个文件, 保存到发布目录中
String path = req.getServletContext().getRealPath("/images/upload/");
//确保上传目录存在
File uploadDir = new File(path);
if (!uploadDir.exists()) {
uploadDir.mkdirs();
}
//获得文件的名字
String fileName = photo.getSubmittedFileName(); // jt.jpeg
//文件后缀
String suffix = fileName.split("\\.")[1];
//重命名
fileName = UUID.randomUUID().toString() + "." + suffix;
//保存
photo.write(path + fileName);
//或取其他数据
String sno = req.getParameter("sno");
//调用业务方法
SingerService singerService = new SingerServiceImpl();
try {
singerService.updatePhoto( Integer.parseInt(sno) , "/images/upload/"+fileName );
} catch (Exception e) {
e.printStackTrace();
}
//响应
resp.getWriter().println("upload success" + sno);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("SB please use post!");
}
}
总结 , 目前这种文件上传方式为同步上传,就是把表单中的数据和图片一起上传提交。后面学习ajax后可以实现异步上传,异步上传就是可以单独先上传图片,然后立即获得上传路径, 最后表单只需要把这个路径和其他字段提交即可。
这种方式,使用一些固定资源文件,方便快捷,不需java编码,把文件通过 超链接 挂出来即可,用户点击超链接即可下载。
问题: 1. 如果下载文件 能够被浏览器解析, 浏览器会自动打开,而不是下载。 2. 中文文件名可能乱码,无法下载。
下载页面
<a href="/download/bootstrap-4.6.0-dist.zip" > 下载zip笔记 </a>
这种方式,需要编写控制器,任何下载需要通过控制器下载,把下载文件名字发给控制器,控制器取读取文件,然后以字节流方式响应可客户端,可以在响应头中添加 头信息,防止浏览器直接打开。
@WebServlet("/download.do")
public class DownloadController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获得下载文件的名字
String fname = req.getParameter("fname");
//IO流读文件
String realPath = req.getServletContext().getRealPath("/download/" + fname);
//下载中文名乱码
fname = URLEncoder.encode(fname,"UTF-8");
//设置响应头
resp.setHeader("Content-Disposition", "attachment; filename="+fname);
//字节流
BufferedInputStream bis = new BufferedInputStream( new FileInputStream(realPath));
byte[] bs = new byte[1024];
int len;
while( (len=bis.read(bs))!=-1){
resp.getOutputStream().write(bs,0,len);
}
resp.getOutputStream().flush();
}
}
resp.setHeader(“Content-Disposition”, “attachment; filename=”+fname); 这就代码比较关键,可以告诉浏览器,下载,而不打开。
下载页面
<a href="/download.do?fname=bootstrap-4.6.0-dist.zip" > 下载zip笔记 a>
图片验证码,是一种为了防止,用户恶意制造大量请求,消耗服务器运算资源,导致服务器卡顿或宕机,当然也可防止,用户暴力破解密码。但是随着图形图像识别技术也来越高,图片验证码基本也没什么用了,Google 已经放弃图片数字验证码。有还是比没有好。
开源界有很多这方面的工具,案例中我们使用 easy-captcha-1.6.2.jar 演示。该工具提供了丰富的验证码类型
// SpecCaptcha 普通验证码
// GifCaptcha 动态gif验证码
// ChineseCaptcha 中文验证码
// ArithmeticCaptcha 数学公式验证码
获得 easy-captcha-1.6.2.jar 包添加到 WEB-INF/lib/easy-captcha-1.6.2.jar
@WebServlet("/captcha.do")
public class CaptchaController extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建一个图片对象 参数1 图片宽度 参数2 图片高度 参数3 字符个数
SpecCaptcha specCaptcha = new SpecCaptcha( 120,40 );
//获得图片中的文字信息, 保存到Session
req.getSession().setAttribute("code",specCaptcha.text());
//图片字节流输出客户端
specCaptcha.out( resp.getOutputStream() );
}
}
网页中 通过 IMG 标签 把 控制器的访问地址给到 IMG 的src 属性即可
<form method="post" action="/login.do" >
<input type="text" name="username" placeholder="输入账号" > <br>
<input type="password" name="password" placeholder="输入密码" > <br/>
<input type="text" style="width: 100px;" name="userCode" placeholder="输入验证码" >
<img src="/captcha.do"><br>
<input type="submit" value="登陆">
form>
需要注意的是,确保获得图片验证码的控制器不要被拦截。
在登陆的控制器中比较 用户输入的验证码 与 Session 中的验证码的 正确性。
@WebServlet("/login.do")
public class LoginController extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取用户输入的验证码
String userCode = req.getParameter("userCode");
//获得Session中正确验证码
String code = req.getSession().getAttribute("code").toString();
if( code.equalsIgnoreCase( userCode ) ){
System.out.println("进一步做登陆");
}else{
System.out.println("验证码错误");
}
}
}
完结 撒花~~~~~~