Struts2第四天
Struts2第四天
昨天:
-
自定义的拦截器:继续methodFilterInterceptor,可以指定哪些方法需要拦截或者不拦截。
Intercepters(配置拦截器),intercepter(自定义拦截器),配置自定义拦截器栈,把自定义的拦截器和默认的拦截器栈,放入自定义拦截器栈
覆盖默认的拦截器栈
-
值栈:root(list):一般都是操作栈顶数据,push方法,set方法(将数据存入到map中,最后将map压入栈顶),map(map):put方法存入到map栈,特点是—key和value。Action的属性可以存入到值栈
取值:[0].top s:property s:property value="key"(从root栈栈顶开始搜索,直到map栈,搜索到的第一个key返回),s:property value="#key":直接从map栈获取数据。
-
Ognl的用法:#--可以从map栈获取数据,包括servlet API中的数据。%--强制解析或者强制不解析。$--配置文件中获取值栈数据
-
Struts的标签: 通用标签(value是一个ognl表达式),表单标签(name是一个ognl表达式)
今天的安排:
-
Struts2的文件上传(fileUpload拦截器)
-
Struts2的文件下载(stream结果集类型)
-
系统环境的准备:系统静态页面的导入、功能分析
-
数据库的创建,原始数据的导入。(单表)
-
员工登录功能。(更改struts标签、表单验证、国际化)
-
员工添加功能。(ajax请求+struts2的json插件,值栈的使用,文件(简历)上传)
-
员工列表查询功能。(查询所有s:iterator标签)
目标:
-
业务功能:登录、添加、列表查询
-
技术点:struts2的基础技术(表单验证、国际化、标签),json插件,文件上传
-
Struts2的文件的上传
-
回顾文件的上传
-
-
jsp-smartupload 组件 Servlet3.0 以后 API内置文件上传API COS 文件上传组件 |
-
Struts2的文件上传机制
Struts2 内部文件上传,默认采用 apache commons-fileupload
Struts2 默认导入文件上传jar包
文件上传很多代码都是一样的,那么还需要我们自己去写吗,有一个拦截器已经帮我们封装了大部分代码
defaultStack 默认拦截器栈,提供 fileUpload的拦截器,用于实现文件上传
-
Struts2的文件上传的实现
步骤一:创建工程环境
创建工厂、导包、配置前端控制器
步骤二:编写upload.jsp 上传文件表单
创建页面
注意:编码类型一定要写好
Upload.jsp
<form action="${pageContext.request.contextPath }/uploadAction" enctype="multipart/form-data" method="post">
文件上传: <input type="file" name="upload">
<input type="submit" value="上传"/>
</form>
提示:也可以使用struts标签
<s:form action="uploadAction" namespace="/" enctype="multipart/form-data" method="post" theme="simple">
文件上传:<s:file name="upload"></s:file>
<s:submit value="上传"/>
</s:form>
Success.jsp:
<body>
<h1>文件上传成功!</h1>
</body>
步骤二:编写action
创建action
内容如下:
public class UploadAction extends ActionSupport{
private File upload;//上传的文件资源,必须和form表单中的上传标签中的name属性一致
private String uploadContentType;//文件类型
private String uploadFileName;//文件名称
public void setUpload(File upload) {
this.upload = upload;
}
public void setUploadContentType(String uploadContentType) {
this.uploadContentType = uploadContentType;
}
public void setUploadFileName(String uploadFileName) {
this.uploadFileName = uploadFileName;
}
@Override
public String execute() throws Exception {
FileUtils.copyFile(upload, new File("f:/upload/uploadFileName"));
return super.execute();
}
}
步骤三:Struts.xml:
<struts>
<!-- 配置常量 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 开启国际化资源文件 -->
<constant name="struts.custom.i18n.resources" value="message"></constant>
<package name="default" extends="struts-default" namespace="/">
<action name="uploadAction" class="cn.itcast.struts.upload.UploadAction">
<result>/upload/success.jsp</result>
</action>
</package>
</struts>
步骤四:测试
上传文件成功
-
文件上传的参数设置
问题:上传文件是不是什么都能上传,不管文件类型和文件大小都能上传?
显然可以设置文件上传的限制
在struts2 文件上传,存在一些限制参数,当违背参数,跳转 input 视图
找到上传的拦截器:通过拦截器中的setter方法获取参数
-
【知识点1】文件大小限制
通过在action中配置拦截器栈中的fileUpload拦截器的参数, 注意:该配置只影响当前的action的文件上传。
【示例】
步骤一:修改struts.xml:设置上传文件的大小为1m,只在当前action中生效
注意:显示上传单个文件大小校验错误的信息使用<s:fielderror/>,因为struts默认超过单个上传文件大小限制的错误属于字段错误。
<package name="default" extends="struts-default" namespace="/">
<action name="uploadAction" class="cn.itcast.struts.upload.UploadAction">
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">1097152</param>
</interceptor-ref>
<result>/upload/success.jsp</result>
<result name="input">/upload/upload.jsp</result>
</action>
</package>
步骤二:Jsp页面:显示错误信息
<body>
<!-- 显示逻辑错误的信息 -->
<s:fielderror/>
<form action="${pageContext.request.contextPath }/uploadAction" enctype="multipart/form-data" method="post">
文件上传: <input type="file" name="upload">
<input type="submit" value="上传"/>
</form>
<hr>
<s:form action="uploadAction" namespace="/" enctype="multipart/form-data" method="post" theme="simple">
文件上传:<s:file name="upload"></s:file>
<s:submit value="上传"/>
</s:form>
</body>
步骤三:测试
上传一个超过2m的文件
点击上传后结果:报错,证明限制起作用了。
步骤四:修改错误信息提示
英文的错误如果看不懂可以通过覆盖配置文件常量来自行修改成中文
更改默认的英文提示:
内置国际化:
找到默认的错误信息:
创建一个国际化文件,将默认的错误信息覆盖:
步骤五:再次测试上传错误类型的文件:错误信息已修改
步骤六:设置文件上传的总大小
<!-- 设置文件上传的总大小为20m-->
<constant name="struts.multipart.maxSize" value="209715200" />
步骤七:测试上传超过总大小的文件,比如上传30M的文件
在页面显示错误信息必须使用<s:actionerror/>,因为struts2默认为这是一个逻辑错误,所以要使用<s:actionerror/>标签
使用<s:actionerror/>标签后错误信息显示如下:
步骤八:将英文的错误信息修改成中文的
还是找到默认信息的配置文件:
通过错误信息的前面几个英文进行查找,可以找到错误信息的key
在message.properties中自定义错误信息:
再次测试上传一个超过总大小限制的文件,比如30m的文件,错误信息显示如下
-
【知识点2】文件类型设定
步骤一:在xml中配置文件类型的限制:
注意:显示文件扩展名校验错误的信息使用<s:fielderror>
<package name="default" extends="struts-default" namespace="/">
<action name="upload" class="cn.itcast.struts.upload.UploadAction">
<interceptor-ref name="defaultStack">
<param name="fileUpload.allowedExtensions">.jpg</param>
<param name="fileUpload.maximumSize">2097152</param>
</interceptor-ref>
<result>/upload/success.jsp</result>
<result name="input">/upload/upload.jsp</result>
</action>
</package>
步骤二:修改页面提示信息:
步骤三:测试
上传一个.docx的文件
点击上传后结果:显示文件后缀名错误
步骤四:修改错误信息提示
如果需要改成中文的错误提示:可以再次到内置国际化信息文件中找到对应的key并在自定义的messages.properties配置文件中配置
配置如下:
步骤五:再次测试
错误提示已改成中文
-
Struts2的文件的下载
-
回顾文件的下载
要点:一个流, 两个头信息
-
使用输入流,读取需要下载的资源文件
-
设置Content-Type头信息:下载文件对应MIME协议的文件类型,通过servletContext.getMimeType(文件名) 获取
-
设置Content-Disposition 头信息:控制下载文件的方式,比如浏览器内部打开或者以附件形式打开,attachment;filename=文件名
-
Struts2的文件下载机制
Struts2 文件的下载,可以通过结果集类型stream来实现。
因此注意:文件下载的action必须返回一个结果集视图,并且该结果集视图的类型必须是stream
download在struts中是个关键字,因此在使用struts.xml配置名称的时候最好不要用,比如:
否则会报错如下:
Stream结果集类型:
内容如下:
-
简单示例
步骤一:action
注意:一定要返回结果集视图
public class DownloadAction extends ActionSupport{
@Override
public String execute() throws Exception {
InputStream inputStream = new FileInputStream(new File("f:/a.txt"));
ActionContext.getContext().put("inputStream", inputStream);
System.out.println("nihao");
return SUCCESS;
}
}
步骤二:struts.xml
<!-- 文件下载 -->
<action name="downloadAction" class="cn.itcast.struts.download.DownloadAction">
<result type="stream"></result>
</action>
步骤三:测试
如上这种写法,其文件的MIME类型以及下载方式已经被固定了,在stream中有默认的方式:
这显然不符合我们实际开发的需求,实际开发中,需要按照下载文件的类型来改变MIME类型,而且一般都是通过附件形式进行下载,见下方复杂示例
-
复杂示例
步骤一:Jsp页面
创建页面
内容如下:
<body>
<a href="${pageContext.request.contextPath }/downloadAction?filename=holiday.jpg">下载图片</a>
<a href="${pageContext.request.contextPath }/downloadAction?filename=尘.mp3">下载mp3</a>
<a href="${pageContext.request.contextPath }/downloadAction?filename=a.txt">下载txt</a>
</body>
步骤二:Action
public class DownloadAction extends ActionSupport{
private String filename;
public void setFilename(String filename) throws Exception {
filename=new String(filename.getBytes("iso8859-1"),"utf-8");
this.filename = filename;
}
@Override
public String execute() throws Exception {
InputStream inputStream = new FileInputStream(new File("f:/"+filename));
//通过文件名称获取mime类型
String contentType = ServletActionContext.getServletContext().getMimeType(filename);
//获取浏览器的类型
String agent = ServletActionContext.getRequest().getHeader("User-Agent");
//附件名称的编码
String encodeDownloadFilename = encodingFileNameUtil.encodeDownloadFilename(filename, agent);
//将文件类型存入值栈
ActionContext.getContext().put("contentType", contentType);
//将输入流存入值栈
ActionContext.getContext().put("inputStream", inputStream);
//以附件形式进行下载
String contentDisposition="attachment;filename="+encodeDownloadFilename;
//将下载类型存入到值栈
ActionContext.getContext().put("contentDisposition", contentDisposition);
return SUCCESS;
}
}
步骤三:Struts.xml:
<!-- 配置文件下载 -->
<action name="downloadAction" class="cn.itcast.struts.download.DownloadAction">
<result type="stream">
<param name="contentType">${#contentType}</param>
<param name="contentDisposition">${#contentDisposition}</param>
</result>
</action>
步骤四:测试
执行结果
步骤五:分析
一个流和两个头,最终其实还是从值栈中通过固定的key查询出来的,因此我们可以直接将数据通过对应的key存入值栈就可以了,而不需要去到struts.xml中配置参数。
Stream代码:
-
项目开发环境的搭建
开发软件:
考虑系统的架构:你要用什么技术、要怎么分层、要用什么组件 。。。
我们的架构:struts2+mysql+jquery+dbutil
-
构建strut2的环境
步骤一:创建工程并导入页面
导入静态页面
效果如下:
步骤二:导入lib,共计16个
创建工具包,导入工具类和配置文件:
struts的lib+dbutil+数据驱动+c3p0
更改数据源配置:将数据源修改成自己的数据库配置
步骤三:配置文件
两个配置文件(web.xml前端控制器-过滤器,核心配置文件struts2.xml)
前端控制器:
Struts.xml
-
环境测试
测试环境
请求地址:
显示如下:
-
开发功能的分析
通过分析页面效果, 要开发功能 :
1、 员工登陆
2、 员工添加 (上传简历 )
3、 员工列表查询 (多条件 组合查询 )
4、 员工编辑(回显,更新)
5、 员工详细信息查看 (简历下载 )
6、 员工删除 (阻止默认事件发生)
7、自定义拦截器
详细功能:
* 要完成的功能: * 把普通的html标签替换为struts2标签 * 登录 * 用户名和密码用struts2验证框架进行验证
* 首页完善 * 显示当前的用户名
* 用户的添加 包含文件上传,配置参数(临时路径总开关 3三个配置 struts2验证框架表单重复提交)
* 用户的条件查询
* 用户的编辑(文件上传)
* 用户的修改 包含文件上传,表单重复提交
* 用户的删除
* 查看 包含文件下载)
* 自定义拦截器(验证用户登录)
|
-
数据库的创建和数据的导入。
准备工具:mysql
-
建立数据库,建立用户,授权
一般企业开发的时候,不会用root用户。root主要用来管理用。
一般我们都建立新的用户和数据,然后对应一个项目。
1.建立数据库usermanage---root先登录
编码是跟你数据库配置有关系。默认值在my.ini的文件中配置的,如果没有配置默认编码,那么建议你手动选择,否则会出现编码问题。
[mysqld] #服务端编码配置 character-set-server=utf8 |
手动选择:
2.建立用户,并且授权
主机:%代表其他任何的机器可以访问,localhost:本机可以访问
将刚才建立的表授权给新建的用户:
命令脚本:
grant all on struts2_em17.* to struts2_em17;
重新用新建的用户登录:
-
导入原始数据(表结构+基本数据)
#用户表 CREATE TABLE S_User( userID INT NOT NULL AUTO_INCREMENT, #主键ID userName VARCHAR(50) NULL, #用户姓名 logonName VARCHAR(50) NULL, #登录名 logonPwd VARCHAR(50) NULL, #密码# sex VARCHAR(10) NULL, #性别(例如:男,女) birthday VARCHAR(50) NULL, #出生日期 education VARCHAR(20) NULL, #学历(例如:研究生、本科、专科、高中) telephone VARCHAR(50) NULL, #电话 interest VARCHAR(20) NULL, #兴趣爱好(例如:体育、旅游、逛街) path VARCHAR(500) NULL, #上传路径(path路径) filename VARCHAR(100) NULL, #上传文件名称(文件名) remark VARCHAR(500) NULL, #备注 PRIMARY KEY (userID) ) ;
#初始化数据:默认用户名和密码是admin INSERT INTO s_user (userID,userName,logonName,logonPwd) VALUES (1,'超级管理员','admin','admin'); |
运行脚本:先选中数据库,然后将脚本粘贴进窗口,然后运行:
刷新用F5
-
创建包层次
-
员工登录功能。
-
需求功能分析
业务流程:当用户输入用户名密码后,如果通过,则跳到首页,如果不通过,则跳回来。
分析:表单校验。比如:不能不输入。要求:用户名和密码必须都输入。
后台逻辑:当登录成功了,将用户放入session。
-
页面标签改成struts标签
目标:要将普通的表单,改造成struts2的表单。
为什么?struts表单,用起来比较方便,简单,而且你用struts开发,用s标签,可以表单回显。
运行发现样式发生变化:
原因是struts2有内置样式,解决方案:将内置的样式去掉。
struts.xml:
-
编写登录功能
-
action
public String login(){
UserService userService = new UserService();
User queryUser=userService.findUser(user);
if (queryUser==null) {
//添加错误提示信息
this.addActionError("用户名或者密码错误!");
return LOGIN;
}else{
//登录成功将信息存入到session中
ServletActionContext.getRequest().getSession().setAttribute("user", queryUser);
return SUCCESS;
}
-
service层
public class UserService {
public User findUser(User user) {
UserDao userDao = new UserDao();
return userDao.findUser(user);
}
}
-
dao层
public class UserDao {
public User findUser(User user) {
QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
String sql = "select * from s_user where logonName=? and logonPwd=?";
try {
return queryRunner.query(sql, new BeanHandler<User>(User.class),
user.getLogonName(), user.getLogonPwd());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException("根据用户名和密码查询用户失败!");
}
}
}
-
Struts.xml
<package name="default" namespace="/" extends="struts-default">
<!-- 用户管理 -->
<action name="user_*" class="cn.itcast.usermanage.web.UserAction" method="{1}">
<result type="redirect">/login/home.jsp</result>
<!-- 登录校验错误跳转视图 -->
<result name="login">/login/login.jsp</result>
</action>
</package>
-
页面添加错误提示
-
测试
登录错误提示:
-
表单校验
配置Xml校验文件:
配置校验文件:一般用局部校验。(一个功能表单对应一个校验,每个表单的提示错误信息可能都不一样。):格式:在action类的包中建立一个文件:action类名-action的名字-validation.xml
步骤一:创建校验文件
校验内容:
<validators>
<field name="logonName">
<field-validator type="requiredstring">
<message key="LogonName.required"></message>
</field-validator>
</field>
<field name="logonPwd">
<field-validator type="requiredstring">
<message key="LogonPwd.required"></message>
</field-validator>
</field>
</validators>
步骤二:配置国际化信息
内容:
步骤三:Action
登录错误的信息提示从国际化信息文件中获取
public String login(){
UserService userService = new UserService();
User queryUser=userService.login(user);
if (queryUser==null) {
//添加错误提示信息
this.addActionError(this.getText("LoginError"));
return LOGIN;
}else{
//登录成功将信息存入到session中
ServletActionContext.getRequest().getSession().setAttribute("user", queryUser);
return SUCCESS;
}
}
步骤四:Struts.xml
开启国际化信息机制
具体配置内容:配置校验失败跳转的结果集视图
<struts>
<!-- 配置一个开发者模式 -->
<constant name="struts.devMode" value="true"></constant>
<!-- 修改strute标签样式为简单样式 -->
<constant name="struts.ui.theme" value="simple"></constant>
<!-- 开启国际化信息机制 -->
<constant name="struts.custom.i18n.resources" value="message"></constant>
<!-- package:包 name:包名,必须保证唯一性 extends:继承struts-default namespace:名称空间 -->
<package name="default" extends="struts-default" namespace="/">
<!-- 设置action -->
<action name="user_*" class="cn.itcast.struts.action.UserAction" method="{1}">
<!-- 登录失败跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<!-- 如果登录成功,跳转到home.jsp -->
<result type="redirect">/login/home.jsp</result>
<!-- 配置字段校验失败跳转的input结果集视图 -->
<result name="input">/login/login.jsp</result>
</action>
</package>
</struts>
步骤五:jsp页面
添加校验错误提示
步骤六:测试
什么都不写提交表单
【扩展】
表单错误信息的局部显示
样式想跟在后面,需要手动更改模版。
-
错误信息补充
扩展补充:
关于struts2的这个三个:区别
-
fieldError 用来存储字段错误的信息,一般用于存放具体的字段校验的错误,会和字段绑定。
-
actionerror:用来存储action范围(request)内错误信息,这个存储与具体字段无关的表单错误信息。
-
主页显示优化
优化一:
页面显示用户名:并没有显示数据库中的用户名,显然是写死了。
找到所在的jsp页面:
将原来写死的数据改成用EL表达式来从session中获取。
优化二:
登录成功后显示页面:
top.jsp页面:
首页重定向:
<!-- 用户登录 -->
<action name="user_*" class="cn.itcast.struts.actoin.UserAction" method="{1}">
<!-- 配置登录成功后需要响应的结果集视图 -->
<result type="redirect">/login/home.jsp</result>
<!-- 登录失败后需要跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<result name="input">/login/login.jsp</result>
</action>
-
员工添加功能。
需求功能分析:将表单数据保存到数据库
-
jquery插件的使用
JQuery有很多插件,帮助我们做一些效果或者功能。
jQuery官方+第三方都提供了一些插件(easyUI)。
官方的插件:jQuery ui
下载:
解压查看所有插件效果(查看index.html即可):
集成方式:
步骤一:导入js库和样式:
步骤二:页面引入:
参考使用:
Datepicker
步骤三:jqueryui.jsp
弹出的插件:
-
表单标签修改成struts标签
将普通的form表单改成struts表单
-
添加用户-表单校验
分析:
给登录名、密码、用户姓名添加一个非空的针对方法的局部表单校验
步骤一:action
新增一个add方法,需要做局部的表单校验
public String add() throws IOException{
System.out.println("新增用户成功!");
return SUCCESS;
}
步骤二:添加xml局部校验文件
步骤三:编写的国际化信息
步骤四:页面上显示错误信息:
步骤五:测试
校验错误跳转input视图,而此时我们应该让其重新跳转到add.jsp,重新新增用户。
错误原因:struts.xml中我们已经配置过一个登录失败时跳转的input视图,现在新增用户校验失败的话也会跳转到之前配置input视图,可是这个input视图配置的结果集并不是我们想要的,我们在新增用户校验失败的时候需要重新跳转到新增用户—add.jsp页面。而一个action中只能配置一个input视图,此时该怎么办?
分析:看workflow拦截器是不是有推荐的解决方案。
核心文件中:拦截器:负责错误页面的跳转:
进入到这个类中发现注释中有使用标签来修改结果集的简介
步骤六:添加input视图跳转的注解
//添加用户
//可以手动更改校验错误发生是跳转的结果集视图
@InputConfig(resultName="addError")
public String add(){
System.out.println("用户新增成功");
return "add";
}
Struts.xml:添加addinput视图的跳转
<!-- 设置action -->
<action name="user_*" class="cn.itcast.struts.action.UserAction" method="{1}">
<!-- 登录失败跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<!-- 如果登录成功,跳转到home.jsp -->
<result type="redirect">/login/home.jsp</result>
<!-- 配置字段校验失败跳转的input结果集视图 -->
<result name="input">/login/login.jsp</result>
<!-- 配置校验失败的结果集视图 -->
<result name="addError">/user/add.jsp</result>
</action>
步骤七:再次测试
结果:校验错误后还是返回add.jsp页面,无误。
-
编写保存数据的代码
保存的时候要注意两个操作:
-
用户信息的保存。
-
上传简历文件的保存。
-
保存用户信息(上传简历除外)
步骤一:Action
//添加用户
//可以手动更改校验错误发生是跳转的结果集视图
@InputConfig(resultName="addError")
public String add(){
UserService userService = new UserService();
userService.add(user);
return "add";
}
步骤二:Service
public void add(User user) {
new UserDao().add(user);
}
步骤三:Dao
public void add(User user) {
QueryRunner queryRunner = new QueryRunner(JDBCUtils.getDataSource());
String sql = "insert into s_user values(null,?,?,?,?,?,?,?,?,?,?,?)";
try {
queryRunner.update(sql, user.getUserName(), user.getLogonName(),
user.getLogonPwd(), user.getSex(), user.getBirthday(),
user.getEducation(), user.getTelephone(),
user.getInterest(), user.getPath(), user.getFilename(),
user.getRemark());
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException("新增用户失败!");
}
}
步骤四:Struts.xml:
<package name="default" extends="struts-default" namespace="/">
<!-- 设置action -->
<action name="user_*" class="cn.itcast.struts.action.UserAction" method="{1}">
<!-- 登录失败跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<!-- 如果登录成功,跳转到home.jsp -->
<result type="redirect">/login/home.jsp</result>
<!-- 配置字段校验失败跳转的input结果集视图 -->
<result name="input">/login/login.jsp</result>
<!-- 配置校验失败的结果集视图 -->
<result name="addError">/user/add.jsp</result>
<!-- 配置新增用户成功后跳转的结果集视图 -->
<result name="add">/user/list.jsp</result>
</action>
</package>
步骤五:测试
测试保存:发现有两个字段没有数据,一个是路径,一个是文件名,这两个是和上传简历有关的字段。
-
上传简历
-
思路分析
-
思考:在简历上传完成之后是不是要保存到一个指定的路径,那么这个路径保存的时候要考虑会不会重复,因此需要给保存的文件设置一个随机的不会重复的路径。
在第四天的课前资料里面有一个工具类,可以自动生成一个随机的文件名和二级目录,可以将该工具类复制到utils包下:
内容如下:
有了随机的目录和文件名,只要再指定一个盘符保存就行了,为了使用方便,这个盘符我们可以设置成一个常量,而这个常量我们也可以创建一个类来调用它,如下:这个常量好处是节省资源,只会初始化一次。
内容如下:其实只是一个保存路径的前半段而已。
上传文件需要注意的要点:
1、form表单提交方式必须是post,enctype属性必须是= multipart/form-data
2、必须有一个上传文件的标签
开始编写上传逻辑
保存分两步进行:
第一步:先保存文件到服务器
依赖于拦截器:
-
编写上传简历业务逻辑
步骤一:action
添加上传文件需要的属性
以上这三个属性原来我们是直接写在action中的,但是显示如果分了三层之后上传的逻辑更加适合写在service层中,而为了传递参数方便,因此我们可以将这个三个属性写到user对象中去并且提供getter和setter方法,这样传递到service层进行业务逻辑处理的时候显然方便很多。。
修改user:
在add方法中添加上传逻辑:
//新增用户
@InputConfig(resultName="addError")
public String add(){
UserService userService = new UserService();
userService.add(user);
return "add";
}
步骤二:service添加上传逻辑
public void add(User user) {
try {
//添加一个用户是否上传文件的判断,避免空指针错误
if (user.getUpload()!=null) {
String uploadFileName = user.getUploadFileName();
//生成一个uuid文件名
String uuid = UploadUtils.generateRandonFileName(uploadFileName);
//生成一个二级目录
String dir = UploadUtils.generateRandomDir(uuid);
//生成一个指定保存简历的路径
String realPath=SystemConstant.Default_DIR+dir+"/"+uuid;
user.setPath(realPath);
user.setFilename(uploadFileName);
File upload = user.getUpload();
//保存简历到指定的路径
FileUtils.copyFile(upload, new File(realPath));
}
new UserDao().add(user);
} catch (IOException e) {
e.printStackTrace();
}
}
步骤三:测试
上传成功
步骤四:设置上传简历的大小和后缀名
文件上传大小的设置:
struts.xml文件中的大小与实际文件大小的关系:1048576(Bytes) = 1024*1024 = 1M实际文件大小。
struts有两个地方来限制文件上传大小的!
1)核心配置文件中的常量
2)在struts.xml中的action标签里配置:
局部的文件上传参数:
<action name="user_*" class="cn.itcast.usermanage.web.UserAction"
method="{1}">
<!-- 设置局部上传文件大小和扩展名 -->
<interceptor-ref name="defaultStack">
<param name="fileUpload.maximumSize">2097152</param>
<param name="fileUpload.allowedExtensions">.doc,.avi,.docx,.txt,.mp3</param>
</interceptor-ref>
<!-- 登录失败跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<result type="redirect">/login/home.jsp</result>
<!-- 登录校验错误跳转视图 -->
<result name="input">/login/login.jsp</result>
<result name="add">/user/list.jsp</result>
<!-- 新增用户校验错误跳转视图 -->
<result name="addError">/user/add.jsp</result>
</action>
或者在package下设置全局的文件上传参数:
<package name="default" namespace="/" extends="struts-default">
<!-- 设置全局上传文件的大小和扩展名 -->
<interceptors>
<interceptor-stack name="mydefault">
<interceptor-ref name="defaultStack">
<!-- 限制上传文件大小 -->
<param name="fileUpload.maximumSize">2097152</param>
<!-- 限制上传文件扩展名 -->
<param name="fileUpload.allowedExtensions">.doc,.avi,.docx,.txt,.mp3</param>
</interceptor-ref>
</interceptor-stack>
</interceptors>
<!-- 覆盖默认拦截器栈 -->
<default-interceptor-ref name="mydefault"></default-interceptor-ref>
<!-- 用户管理 -->
<action name="user_*" class="cn.itcast.usermanage.web.UserAction"
method="{1}">
<!-- 登录失败跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<result type="redirect">/login/home.jsp</result>
<!-- 登录校验错误跳转视图 -->
<result name="input">/login/login.jsp</result>
<result name="add">/user/list.jsp</result>
<!-- 新增用户校验错误跳转视图 -->
<result name="addError">/user/add.jsp</result>
</action>
</package>
上面局部和全局的两选其一即可
步骤三:测试
上传一个4M的文件:报错出现内置错误提示
问题:文件大小在全局设置和局部设置,这两种有什么区别:
-
全局常量配置的struts.multipart.maxSize掌控整个项目所上传文件的最大的Size(默认2m),超过了这个size,后台报错,程序处理不了如此大的文件。fielderror里面会有如下的提示:
the request was rejected because its size (16272982) exceeds the configured maximum (9000000)
或者如下:
又或者服务器系统就直接抛出没有响应的错误。
或者
-
action中配置的,是针对一次上传的文件大小。这个大小不能超过全局常量的配置,否则要么超过了报错,要么无效。
当上传的文件在struts.multipart.maxSize和maximumSize之间时,系统提示:
File too large: file "MSF的概念.ppt" "upload__5133e516_129ce85285f__7ffa_00000005.tmp" 6007104
结论:全局一般设置大一些,局部的一般根据要求设置。
【错误信息的国际化覆盖】
找到错误信息提示:
覆盖:将该错误提示信息写如message.properties国际化文件中
用中文覆盖原提示即可
再次测试:
-
添加功能优化-异步请求用户名是否重复
需求:如果用户名在数据库中已经存在,那么我将提醒用户,并且不允许提交。
技术:ajax
开发分析:失去焦点后,登录名后面提示,提交按钮禁用掉。
修改页面,增加ajax的前端代码:使用异步请求接收响应的json数据。
问题:为什么不能直接将数据存入值栈,然后页面获取值栈信息进行判断用户是否存在呢?
原因:异步请求,当回调函数获取data数据的时候,响应已经结束了,而值栈的生命周期是从一个请求开始到响应结束,因此值栈的生命周期已经过了,值栈内没有数据了。
struts内置了json的转换器(能够自动将java对象转换成json,并且可以自动放入响应reponse中)。struts2-json-plugin插件中。
能自动将值栈中的某个对象,自动转换成json,并且自动放入响应。
我们就只需要将要异步请求的结果,放入值栈就可以了。插件会自动去拿。
注意:需要修改<package name="default" namespace="/" extends="json-default">
-
导入json插件包
-
页面代码
首先在登录名标签下面添加一个span标签用来添加提示信息
其次可以通过该标签的html来添加提示信息。
$(document).ready(function(){
//使用class属性处理 'yy-mm-dd' 设置格式"yyyy/mm/dd"
$('#birthday').datepick({dateFormat: 'yy-mm-dd'});
$("#userAction_save_do_logonName").blur(function(){
$.post("${pageContext.request.contextPath}/user_checkUserExist",{"logonName":$(this).val()},function(data){
if(!data.isExist){
$("#checkUser").html("<font color='green'>可以注册!</font>");
$("#userAction_save_do_submit").removeAttr("disabled");
}else{
$("#checkUser").html("<font color='red'>登录名已存在!</font>");
$("#userAction_save_do_submit").attr({"disabled":"disabled"});
}
});
});
});
-
编写异步请求Action
/**
* 异步请求校验登录名是否存在
* @return
*/
//异步请求校验,检查登录名是否存在
public String checkUserExist(){
UserService userService = new UserService();
User queryUser=userService.checkUserExist(user);
Map map = new HashMap();
if (queryUser==null) {
map.put("isExist", false);
}else{
map.put("isExist", true);
}
//第一种转换成json数据的方法:压入栈顶
/*ActionContext.getContext().getValueStack().push(map);*/
//第二种转换成json数据的方法:将数据存入到值栈
ActionContext.getContext().put("json", map);
return "checkUserExist";
}
-
Struts.xml
修改package的继承包:
配置结果集:
<!-- 用户登录 -->
<action name="user_*" class="cn.itcast.struts.action.UserAction" method="{1}">
<!-- 登录失败跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<!-- 如果登录成功,跳转到home.jsp -->
<result type="redirect">/login/home.jsp</result>
<!-- 配置字段校验失败跳转的input结果集视图 -->
<result name="input">/login/login.jsp</result>
<!-- 配置校验失败的结果集视图 -->
<result name="addError">/user/add.jsp</result>
<!-- 配置新增用户成功后跳转的结果集视图 -->
<result name="add">/user/list.jsp</result>
<!-- 转换成json数据必须使用json的结果集类型:第一种写法 -->
<!-- <result name="checkUserExist" type="json"></result> -->
<!-- 转换成json数据必须使用json的结果集类型:第二种写法 -->
<result name="checkUserExist" type="json">
<param name="root">json</param>
</result>
</action>
-
测试
测试无误
-
分析
打开json插件包下的struts-plugin.xml
原理:
怎么进行响应数据?
响应的数据怎么来的?
由此原理分析可见,响应json数据有两种写法
-
如果root属性存在,则将根据root属性的值在值栈中寻找对应的值;(推荐)
-
如果root属性不存在,则直接将栈顶的元素拿到。
步骤三:测试
测试效果和第一种方法一致。
问题:为什么不在struts.xml中使用$如下取数据?
区别:
${ognl表达式},你在xml等配置文件中,直接要获取值栈中的某个值的时候,用这个。
而在源码中需要的root是一个参数,这个参数是一个字符串:
-
员工列表查询功能。
无条件查询
分析功能:
将数据从数据库查询出来,放入值栈,页面使用struts的循环标签(<s:iterator>)进行打印即可。
-
页面请求
/login/left.jsp
-
编写action
/**
* 查找所有用户
* @return
*/
//查询所有用户
public String list(){
UserService userService = new UserService();
List<User> userList=userService.queryList();
//将查到的所有用户存入到值栈
ActionContext.getContext().put("userList", userList);
return "list";
}
Service:
// 查询所有用户
public List<User> queryList() {
UserDao userDao = new UserDao();
List<User> userList = userDao.queryList();
return userList;
}
dao:
//查询所有用户
public List<User> queryList() {
QueryRunner runner = new QueryRunner(JDBCUtils.getDataSource());
String sql = "select * from s_user";
try {
return runner.query(sql, new BeanListHandler<User>(User.class));
} catch (SQLException e) {
throw new RuntimeException("异步校验检查用户是否存在失败!");
}
}
-
List.jsp页面显示
导入strut是标签
将原来写死的数据删除后替换如下:
<s:iterator value="userList" var="user">
<tr onmouseover="this.style.backgroundColor = 'white'"
onmouseout="this.style.backgroundColor = '#F5FAFE';">
<td style="CURSOR: hand; HEIGHT: 22px" align="center"
width="18%">
<s:property value="logonName"/>|<s:property value="#user.logonName"/>|${logonName }
</td>
<td style="CURSOR: hand; HEIGHT: 22px" align="center"
width="17%">
<s:property value="userName"/>|<s:property value="#user.userName"/>|${userName }
</td>
<td style="CURSOR: hand; HEIGHT: 22px" align="center"
width="8%">
<s:property value="sex"/>|<s:property value="#user.sex"/>|${user.sex }
</td>
<td style="CURSOR: hand; HEIGHT: 22px" align="center"
width="23%">
<s:property value="telephone"/>|<s:property value="#user.telephone"/>|${telephone }
</td>
<td style="CURSOR: hand; HEIGHT: 22px" align="center">
<s:property value="education"/>|<s:property value="#user.education"/>|${education }
</td>
<td align="center" style="HEIGHT: 22px">
<a href="${pageContext.request.contextPath}/user/edit.jsp?userID=15">
<img src="${pageContext.request.contextPath}/images/i_edit.gif" border="0" style="CURSOR: hand">
</a>
</td>
<td align="center" style="HEIGHT: 22px">
<a href="${pageContext.request.contextPath}/user/view.jsp?userID=15">
<img src="${pageContext.request.contextPath}/images/button_view.gif" border="0" style="CURSOR: hand">
</a>
</td>
<td align="center" style="HEIGHT: 22px">
<a href="${pageContext.request.contextPath}/user/list.jsp?userID=15">
<img src="${pageContext.request.contextPath}/images/i_del.gif" width="16" height="16" border="0" style="CURSOR: hand">
</a>
</td>
</tr>
</s:iterator>
-
struts.xml
当新增用户成功之后,跳转回查询列表页面:
<!-- 设置action -->
<action name="user_*" class="cn.itcast.struts.action.UserAction" method="{1}">
<!-- 登录失败跳转的结果集视图 -->
<result name="login">/login/login.jsp</result>
<!-- 如果登录成功,跳转到home.jsp -->
<result type="redirect">/login/home.jsp</result>
<!-- 配置字段校验失败跳转的input结果集视图 -->
<result name="input">/login/login.jsp</result>
<!-- 配置校验失败的结果集视图 -->
<result name="addError">/user/add.jsp</result>
<!-- 配置新增用户成功后跳转的结果集视图 -->
<result name="add">/user/list.jsp</result>
<!-- 转换成json数据必须使用json的结果集类型:第一种写法 -->
<!-- <result name="checkUserExist" type="json"></result> -->
<!-- 转换成json数据必须使用json的结果集类型:第二种写法 -->
<result name="checkUserExist" type="json">
<param name="root">json</param>
</result>
<!-- 查询所有用户成功后条件结果集视图 -->
<result name="list">/user/list.jsp</result>
</action>
-
测试
新增用户后跳转到list.jsp,并且显示所有用户信息,测试无误。
-
重点和小结
逻辑执行的流程图:
小结
作业:
1、今天的练习。
2、上传简历的添加文件大小和文件类型的限制。
3、做完之后:将条件查询做完。。*(就是一个sql拼接,改表单-数据模型有对应的属性)