第二章.创建web项目.
file->new->web project.
设置 java version 1.8
设置 Target Tomcat8.0
web项目下面src JAVA源码目录
WebRoot 网站目录
WEB-INF classes及lib目录.
webroot下添加HTML.
如何创建一个web项目,了解了web项目的一般结构.
2.2部署web项目.
1.创建web项目.
在浏览器打开网页时, 汉字出现乱码?
解决办法:手工修改html,在<head>里 添加 <meta charset='UTF-8' />
在tomcat目录下,<Host>标签内最后添加<Context path="/demo" docBase="E:\JavaWeb\demo0202\WebRoot" />
其中, docBase 指向 WebRoot目录的路径 ,path是网站的映射路径
注意是WebRoot的目录,不是项目目录
第三章 Servlet
servlet 服务小程序.
java web很重要的功能.
在src下面的packge下面新建一个servlet
每次记得修改tomcat的server.xml的docBase
Servlet运行原理
get/servlet
浏览器(客户端)--------------->Tomcat(服务器)
<---------------
200 OK 资源内容
tomcat在接收到客户端请求后,会根据请求的资源路径URI,来分派给不同的Servlet来处理
例如: /SimpleServlet ->my.SimpleServlet
3.2 创建时间服务
1.创建showtimeservlet服务.
2.修改@WebServlet("/ShowTimeServlet")->@WebServlet("/showtime") //修改的就是uri的地址.
3.doGet代码修改.
response 是应答,request是请求.
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestr =sdf.format(new Date()); //当前时间
response.getWriter().write("Now:" + timestr); //暂时不用中文避免乱码.
}
注意:
1.uri区分大小写.
servlet运行原理
get/servlet
浏览器(客户端)--------------->Tomcat(服务器)
<---------------
200 OK 时间
tomcat在接收到客户端请求后,会根据请求的资源路径URI,来分派给不同的Servlet来处理
例如: /SimpleServlet ->my.SimpleServlet
/showtime->my.ShowTimeServlet.
doGet()可以修改返回数值和方法.
3.3 生成HTML
添加一个Servlet,用于生成HTML页面.
-添加HtmlServlet
-映射URI为 /test/1.html (称为映射路径)
//映射路径 @WebServlet("/test/1.html")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.setCharacterEncoding("UTF-8"); //设置字符集编码
response.setContentType("text/html"); //设置内容格式
PrintWriter out =response.getWriter();
out.write("<html><body> 如何<html><body>"); //返回客户端的数据
}
servlet运行原理
get/test/1.html
浏览器(客户端)--------------->Tomcat(服务器)
<---------------
200 OK 时间
1.一个URI 可能是一个文件,也可能是一个Servlet
2.客户端不能区分,也没必要区分
3.如果既不是Servlet,又不是文件,返回404错误
4.如果即是Servlet,又是文件呢.
3.4 生成JavaScript
Jscript
@WebServlet("/sample.js")
public class JscriptServlet extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
response.setCharacterEncoding("UTF-8"); // 设置字符集编码
response.setContentType("text/plain"); // 设置内容格式
PrintWriter out = response.getWriter();
// 返回一行javaScript文本
out.write("var author='仁豪'; ");
out.write("var name='yyf'; ");
}
}
写一个.java文件
创建html,导入juery.js,
<html>
<head>
<title>RH.html</title>
<script type="text/javascript" src="js/jquery-3.3.1.min.js" ></script>
<script type="text/javascript" src="sample.js" ></script>
</head>
<body>
<div class='main' style="height: 35px; ">
</div>
<div class='ame'>
</div>
</body>
<script>
$('.main').html ( author + author );
$('.ame').html (name + name);
</script>
</html>
此外tomcat的配置
<Context path="/demo" docBase="E:\javaweb\demo0201\WebRoot"
reloadable="true" />
docBase写到WebRoot一级目录才行.
4.1 POJO
简单的java类.
-属性:简单类型 int String boolean...
-方法:Getter/Setter
POJO类可以重写toString()
4.2 JSON javascript对象的文本化表示
jar包放在WebRoot下面的WEB-INF 下面的lib里面,自动bulid path
JSON多用于数据传输
{
"id":20111111, //示例
"name":"仁豪",
"phone":"1300099";
}
使用JSON-ORG 或者jsonlib来操作JSON
其中,大括号表示一个JSON对象。在这个对象里,包含4个字段。
例如,
"id" 字段的值为 20180001,是数字类型 ( Number )
"name" 字段的值为 "邵", 是字符串类型 ( String )
"sex" 字段的值为 true,是布尔类型 (Boolean)
可以很直观的发现,
如果值是一个String,则应该用双引号,如"邵发"
如果值是一个Number,则不加双引号,如20180001
如果值是一个Boolean,则应该是 true 或者是 false, 不加双引号
加入JSON-java JSON-lib
JSONObject jobj = new JSONObject();
jobj.put("id", 20180001); // Integer
jobj.put("name", "邵发"); // String
jobj.put("sex", true); // Boolean
jobj.put("cellphone", "13810012345"); // String
String jsonstr = jobj.toString(2);
System.out.println(jsonstr);
JSONObject里的字段显示顺序是无关的,谁先上、谁在下不影响最终结果。
// POJO -> JSONObject
// 如果是POJO对象 ( 已经添加了 getter ),则可以直接转成JSONObject
public static void test3()
{
Student stu = new Student("邵发", 1238909, true, "13810012345");
JSONObject j = new JSONObject(stu);
String jsonstr = j.toString(2); // 缩进2
System.out.println(jsonstr);
}
JSON的语法格式
用JSON可以表示Object信息,以一对大括号包围,里面可以多个字段。
例如:
{
"name": "邵发",
"id": 1208439,
"sex": true,
"phone": "13810012345"
}
语法要点:
- 以大括号包围
- 字段名称要加双引号
- 字段的值可以为String, Integer, Boolean, Array, null 等类型
- 每个字段以逗号分隔 (最后一个字段末尾不能加逗号)
- 字段的顺序无关
注:JSONObject在概念上对应于Java里的Map
public class TestJson
{
public static void test1()
{
Student pojo =new Student(20180001,"仁豪",true,"13800000");
JSONObject jobj =new JSONObject(pojo);
String js =jobj.toString(2); //縮放為2是
System.out.println(js);
}
public static void test2()
{
ArrayList<Student> students = new ArrayList();
students.add(new Student(20180001,"仁豪",true,"13800000"));
students.add(new Student(20180002,"x",true,"138000001"));
students.add(new Student(20180003,"y",true,"138000002"));
students.add(new Student(20180004,"z",true,"138000003"));
JSONArray jarray =new JSONArray(students);
System.out.println(jarray.toString(2));
}
public static void main(String[] args)
{
test2();
}
}
直接添加一个main方法,单元测试.
4.3使用json
public class StudentQuery extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
ArrayList<Student> students = new ArrayList();
students.add(new Student(20180001,"仁豪",true,"13800000"));
students.add(new Student(20180002,"x",true,"138000001"));
students.add(new Student(20180003,"y",true,"138000002"));
students.add(new Student(20180004,"z",true,"138000003"));
JSONArray jarray =new JSONArray(students);
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain"); //設置內容格式
PrintWriter out = response.getWriter();
//返回javascript文本
out.write(jarray.toString(2));
}
}
4.4 web项目的调试
java调试的一般方法.
1.打印调试.
2.单步调试
两者结合使用.
单步调试.
以debug方式运行服务器
点debug下面的debug configurations
选择tomcat8.0下面的source(来源)
add,javaproject
勾选目标project文件夹,然后ok ,apply,close.
然后在代码添加断点,在浏览器输入地址.
myeclipse弹出提示是否切换debug页面.
如果关闭,在Windows,show view里面找
source not found ,第三方jar不用管.
web项目的调试,被调试的主体其实是tomcat程序,tomcat程序会间接的调用我们的servlet代码.
第五章 5.1 数据查询.
查询和显示数据库中的数据.
模拟:一个假的数据库.
DemoDb:用于模拟一个数据库.
实现一个查询功能,可以返回所有数据行.
URL查询.
@WebServlet("/QueryAll")
public class QueryAll extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
List<Student> rows = DemoDb.i.list();
// List -> JSONArray
JSONArray result = new JSONArray(rows);
// 返回应答数据
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
Writer writer = response.getWriter();
writer.write( result.toString(2));
writer.close();
}
}
5.2 URL参数
按学号范围进行查询.
比如,查询20180001-20180004.
客户端需要把请求的参数发给服务器.
请求可以附在URL末尾
例如:http://127.0.0.1:8080/demo/QueryById?from=20180001&to=20180004
URL参数的写法规则:
1.以问号引导QueryById?xxxxx
2.多个参数对中间用&分开
示例:?key1=value1&key2=value2&key3=value3
3.中文参数比较复杂.
使用request.getParameter("...")获取URL的参数.
@WebServlet("/QueryById")
public class QueryById extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
//从request 里取得参数
String strFrom = request.getParameter("from");
String strTo = request.getParameter("to");
//参数处理
int from = Integer.valueOf(strFrom);
int to = Integer.valueOf(strTo);
//数据查询
List<Student> rows = DemoDb.i.list(from, to);
//List->JSONArray
JSONArray result = new JSONArray(rows);
//返回应答数据
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
Writer writer = response.getWriter();
writer.write(result.toString(2));
writer.close();
}
}
5.3 HTTP网络抓包演示
请求/应答模式.
Wireshark 网络鲨鱼.
可以抓取本机和其他机器的数据交互.
HTTP协议:应答数据.
HTTP/1.1 200状态码.
200-OK,可以访问
403-Forbidden 无权访问
404-Not Found 资源不存在
Content-Length:287
表示正文部分的长度为287字节
正文内容
头部按行定义,每行末行为\r\n
头部与正文之间有一个空行.
5.4 Chrome浏览器抓包
请求/应答 模式.
-F12,开发者工具.
-切换到network面板.
headers表示请求 response应答.
第六章
6.1 AJAX
后台提供的服务(接口),前台如何调用?
http://127.0.0.1:8080/demo6/QueryAll
http://127.0.0.1:8080/demo6/QueryById?from=1&to=10000000
前端希望取得后台数据,并且显示在页面上.
AJAX,一种在网页上调用后台接口的方式.
后台接口:指后台代码实现的服务
类似 /demo/QueryAll /demo/QueryById
jQuery里提供的AJAX调用方法.
示例: $.ajax({...参数...})
其中,
-$.ajax()等同于jQuery.ajax()
-参数里面是一个JS对象
test.html
<!DOCTYPE html>
<html>
<head>
<title>test.html</title>
<meta charset='UTF-8' >
<script type="text/javascript" src="js/query.min.js"></script>
</head>
<body>
<div class='main'>
<div class='form'>
<button class='commit' onclick="query()">查询</button>
</div>
</div>
</body>
<script>
function query() {
//juery ajax调用的一般写法
//$.ajax() 或写成jQuery.ajax()是一样的
$.ajax({
type:'GET', //请求类型GET/POST
url:'QueryAll', //服务URI,使用相对地址
dataType:'json', //期望服务器返回的数据类型
success:function(resp){ //已经将服务器返回的数据转成JS对象
console.log(resp);
}
});
}
</script>
</html>
$.ajax()参数是一个JS对象,其中的属性:
type:'GET'
url:接口地址,使用相对路径
success:当服务器返回应答时,调用此function处理(回调方法)
注意:url容易写错!不要加IP,也不要加前缀.
6.2 处理返回的数据.
显示返回的数据
当AJAX完成时,回调方法被调用,前端页面应处理并显示服务器的返回的数据.
演示:
-可以在HBuilder里进行HTML页面编辑
-最基本的JS字符串拼接处理.
<script>
function query()
{
$.ajax({
type:'GET', /* 请求类型 GET / POST */
url:'QueryAll', /* 服务URI, 用相对地址 */
dataType: 'json', /* 期望服务器返回的数据类型 */
success: function(resp){ /* 已经将服务器返回的数据转成 JS对象 */
//console.log(resp); //将console.log(resp)
showResult(resp); //替换为showResult(resp)方法
}
});
}
// 格式化数据并显示
function showResult(result)
{
var target = $(".main .content tbody");
target.html(""); // 清空
for(var row of result)
{
var str = "<tr>"
+ "<td>" + row.id + "</td>"
+ "<td>" + row.name + "</td>"
+ "<td>" + ( row.sex?'男':'女') + "</td>"
+ "<td>" + row.phone + "</td>"
+ "</tr>"
;
target.append( str );
}
// 如果没有数据,则把下面的提示显示出来
if(result.length > 0)
$(".main .content .empty").hide();
else
$(".main .content .empty").show();
}
</script>
6.3 参数查询.
在用ajax()访问后台接口时,可以附加请求参数
示例:
$.ajax({
type:'GET',
url:'QueryById',
data:req,
....
}
1 添加用户输入控件
<div class='toolbar'>
<input type='text' class='from' placeholder="开始学号" />
-
<input type='text' class='to' placeholder="结束学号" />
<button onclick='query()'> 查询 </button>
</div>
2 ajax() 里添加请求参数
function query()
{
// 请求参数
var req = {};
req.from = $('.toolbar .from').val().trim(); //.trim()去掉两边的空格.
req.to = $('.toolbar .to').val().trim();
$.ajax({
type:'GET', /* 请求类型 GET / POST */
url:'QueryById', /* 服务URI, 用相对地址 */
data: req, /* 附加请求参数 */
dataType: 'json', /* 期望服务器返回的数据类型 */
success: function(resp){ /* 已经将服务器返回的数据转成 JS对象 */
//console.log(resp);
showResult(resp);
}
});
}
注: ajax()会自动的把 data 里的数据构造成 ?from=xxx&to=yyy 的形式附加在 URL后面
6.4 中文URL编码
示例:添加一个QueryByName服务,实现按学生的姓名查询.
此时,需要把一个中文参数传递到后台.
//后台的姓名查询servlet
@WebServlet("/QueryByName")
public class QueryByName extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
//从request里取得参数
String filter = request.getParameter("filter");
//数据查询
List<Student> rows = DemoDb.i.list(filter);
//List->JSONArray
JSONArray result = new JSONArray(rows);
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
Writer writer= response.getWriter();
writer.write( result.toString(2));
writer.close();
}
}
//前端script
function query()
{
// 请求参数
var req = {};
req.filter = $('.toolbar .filter').val().trim(); //.trim()去掉两边的空格.
$.ajax({
type:'GET', /* 请求类型 GET / POST */
url:'QueryByName', /* 服务URI, 用相对地址 */
data: req, /* 附加请求参数 */
dataType: 'json', /* 期望服务器返回的数据类型 */
success: function(resp){ /* 已经将服务器返回的数据转成 JS对象 */
//console.log(resp);
showResult(resp);
}
});
}
中文不能直接放在URL里,
例如:http://xx.x.x/QueryById?filter=张
这样是不支持的,必须把中文字符转成百分号形式的编码.
在Chrome里观测Http交互数据.
URL编码:
'张'->'%E5%BC%A0'
URL解码:
'%E5%BC%A0'->'张'
注:以上均为UTF-8编码.
public class UTF
{
// '张'->'%E5%BC%A0'
public static void testEncode() throws Exception
{
String str = "张";
String query = URLEncoder.encode(str, "UTF-8");
System.out.println("编码后:" + query);
}
// '%E5%BC%A0'->'张'
public static void testDecode() throws Exception
{
String str = "%E5%BC%A0";
String query = URLDecoder.decode(str, "UTF-8");
System.out.println("编码后:" + query);
}
public static void main(String[] args) throws Exception
{
testEncode();
testDecode();
}
}
小结:
-需要明白URL里的百分号形式编码
-在框架未能正确处理URL编码的时候,应该会自己实现中文的URL编码/解码
6.5 异步调用
ajax()默认是异步方式:
-不等待服务器返回结果,立刻返回.
-当服务器返回结果时,调用回调方法进行处理.
可以想象,与服务器的交互实在另外一个线程里处理的.
6.6 浏览器抓包调试
前端问题,还是后台问题,如何界定
第一步:浏览器抓包
确定问题是前台还是后台
第二步:单步调试
如果前端问题,在前端单步调试JavaScript
如果后台问题,在后台单步调试Java代码.
第七章
7.1表单
表单,是一种最原始的前后台数据交互方式.
<form method='POST' action='ServiceUri'>
<input type='text' name='xxx'>
...
<input type='submit'/>
</form>
例:
<div class='main'>
<!-- method: GET/POST,通常为POST action: 即后台接口相对路径 -->
<form method='POST' action='AddStudent' > //添加一个AddSutdent.java的Servlet.
学号: <input type='text' name='id' /> <br> <!-- name: 即参数名 -->
姓名: <input type='text' name='name' /> <br>
手机: <input type='text' name='phone' /> <br>
<select name='sex' >
<option value='male'> 男 </option>
<option value='female'> 女 </option>
</select> <br>
<input type='submit' value='提交' /> <!-- type=submit表示自动执行提交功能 -->
</form>
</div>
后台servlet,注意这里使用的是servlet的doPost方法.
@WebServlet("/AddStudent")
public class AddStudent extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// 因为请求字段里的中文数据,所以要显式设置一下字符编码
request.setCharacterEncoding("UTF-8");
// 从请求里提取参数字段的值
int id = Integer.valueOf(request.getParameter("id"));
String name = request.getParameter("name");
String phone = request.getParameter("phone");
boolean sex = "male".equals(request.getParameter("sex"));
// 添加到数据库
Student s = new Student(id, name, sex, phone);
DemoDb.i.add( s );
// 返回应答数据
JSONObject jresp = new JSONObject();
jresp.put("error", 0); // 错误码,0表示成功
jresp.put("reason", "OK"); // 错误原因描述, 如果没有错误则提示OK
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
Writer writer = response.getWriter();
writer.write( jresp.toString(2));
writer.close();
}
}
添加一个servlet处理表单请求:
对应关系:
AddSutdent->对应action='AddStudent'
doPost()->对应method='POST'
request.getParameter("xxx")->对应name='xxx'
介绍了表单方式来提交数据.
此种方法,以后基本不会用到.
7.2 表单抓包
POST方式的特点:
1.第一行为POST
2.数据时放在HTTP正文部分的提交数据
3.正文仍要用URL编码
7.3 AJAX POST
使用ajax()也可以发送POST请求
演示:
$.ajax({
type:'post', //请求类型GET/POST
url:'AddStudent',//服务URI,相对地址
data:req, //附加请求参数
...
});
示例:
<body>
<div class='main'>
<div class='info'>
学号:<input type='text' class='id' /> <br>
姓名:<input type='text' class='name' /> <br>
手机:<input type='text' class='phone' /> <br>
性别:<select class='sex'>
<option value='male'> 男 </option>
<option value='female'> 女 </option>
</select> <br>
<button onclick='doSubmit()'> 提交</button>
</div>
</div>
</body>
<script>
function doSubmit()
{
//请求参数
var f = $('.info');
var req={};
req.id = $('.id,f').val().trim();
req.name = $('.name,f').val().trim();
req.phone= $('.phone,f').val().trim();
req.sex = $('.sex,f').val().trim();
$.ajax({
type:'POST', //请求类型GET/POST
url:'AddStudent', //服务URI,相对地址
data:req, //附加请求参数
dataType:'json', //期望服务器返回的数据类型
success:function(resp){ //已经将服务器返回的数据转成JS对象.
console.log(resp);
if(resp.error == 0)
alert('成功');
else
alert('出错:' + resp.reason);
},
error:function(jqXHR,textStatus,errorThrown)
{
alert("错误:"+jqXHR.status);
}
});
}
</script>
以后,将以$.ajax()来发送post
第八章 RESTful
8.1 RESTful
RESTful,一种通用的前台后台交互方式
(注:此术语本身没哟严格的定义)
RESTful一般是指这种方式:
1.使用HTTP POST(或者GET)进行数据交互
2.请求数据和应答数据均为JSON格式(或XML)
客户端 请求数据:JSON给服务器
服务器 应答数据:JSON给客户端
请求和应答都是JSON格式,其交互数据为JSON格式,则称为RESTful.
8.2 RESTful的接口实现
前端:在发送请求时,把请求转成JSON字符串
var jsonstr = JSON.stringify(req);
$.ajax({
type:'POST',
url:'AddStudent',
data:jsonstr, //JSON字符串
});
RESTful后台实现
后台:手工读取请求数据,转成JSONObject,然后再做处理
String reqText = readAsText(request.getInputStream(),"UTF-8");
JSONObject jreq = new JSONObject(reqText);
实现AddStudent
@WebServlet("/AddStudent")
public class AddStudent extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
// 读取请求数据, 转成字符串
String reqText = readAsText(request.getInputStream(), "UTF-8");
// 转成 JSON
JSONObject jreq = new JSONObject(reqText);
// 处理请求数据
int id = jreq.getInt("id");
String name = jreq.getString("name");
String phone = jreq.getString("phone");
boolean sex = "male".equals( jreq.getString("sex"));
Student s = new Student(id, name, sex, phone);
DemoDb.i.add( s );
// 返回应答数据
JSONObject jresp = new JSONObject();
jresp.put("error", 0); // 错误码,0表示成功
jresp.put("reason", "OK"); // 错误原因描述, 如果没有错误则提示OK
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
Writer writer = response.getWriter();
writer.write( jresp.toString(2));
writer.close();
}
// 流操作在后面的《网络编程篇》里讲解,属于高级课程,暂不要求大家掌握
// 大家只需要知道 这个方法实现的功能就行:功能就是从streamIn里读取数据转成字符串
public String readAsText(InputStream streamIn, String charset)
throws IOException
{
ByteArrayOutputStream cache = new ByteArrayOutputStream(1024*16);
byte[] data = new byte[1024];
while (true)
{
int n = streamIn.read(data); // n: 实际读取的字节数
if(n < 0) break; // 连接已经断开
if(n == 0) continue;// 数据未完 // TODO: 要防止超时
// 缓存起来
cache.write(data, 0, n);
if(cache.size() > 1024*512) // 上限, 最多读取512K
break;
}
return cache.toString(charset);
}
}
前端使用ajax(),后台使用servlet.
8.3 RESTful附加URL参数的处理
一般来说,使用RESTful接口时,所有的请求参数直接放在请求数据JSON中即可
但有的时候,还有少许参数是放在URL里的.
前端处理
在AJAX请求里,需要把参数字符串附加在URL末尾
$.ajax({
type:'POST',
url:'AddStudent?mode=admin&token=12930289',
data:jsonstr, //json字符串
.....
});
后台处理
在Servlet里,需取出查询字符串,自己解析
String query = request.getQueryString();
HashMap queryParams = parseQuery(query);
// 读取请求数据, 转成字符串
String reqText = readAsText(request.getInputStream(), "UTF-8");
// 转成 JSON
JSONObject jreq = new JSONObject(reqText);
//URL末尾,由问号引导的字符串(在RESTful中也可能有部分参数在URL里)
//例如 mode=admin&token=12930289
String query=request.getQueryString();
HashMap<String,String> queryParms= parseQuery(query);
public HashMap<String,String> parseQuery(String query)
{
HashMap<String,String> params = new HashMap<String,String>();
String[] ppp = query.split("&"); // 用&分隔
for(String p : ppp)
{
String[] kv = p.split("="); // key=value
String key = kv[0];
String value = "";
if(key.length() > 1) value = kv[1]; // 有时候参数里传的是空值
params.put(key, value); // TODO: value 视情况进行 URLDecoder
}
return params;
}
8.4 通用RESTful服务框架.
AfRestfulService:实现RESTful服务接口的支持的通用基础类.
使用方法.
1.新建一个子类,继承于AfRestfulService
2.重写execute()方法
3.添加映射路径@WebService("/path_of_your_service")
添加一个class AddBookService extends AfRestfulService
由于继承于一个抽象类,需要添加add unimplemented methods
重写execute()方法,由于是创建的class类,需要手动添加映射路径@WebServlet("/AddBook")
@WebServlet("/AddBook")
public class AddBookService extends AfRestfulService
{
@Override
protected Object execute(HttpServletRequest httpReq,
HttpServletResponse httpResp,
JSONObject jreq,
HashMap<String, String> queryParams) throws Exception
{
String title = jreq.getString("title");
String author = jreq.getString("author");
String isbn = jreq.getString("isbn");
String press = jreq.getString("press");
// 后台处理部分 .. 省略 ...
System.out.println("书名:" + title + ",作者: " + author
+ ", ISBN: " + isbn + ",出版社:" + press);
return null; // JSONArray, JSONObject ,
}
}
介绍了AfRestfulService的使用,可以用于快速实现一个RESTful服务接口
所有网站相关的基础API封装在af.web.*里
9.1 学生管理-新增与列表
1.1 后台
添加 StudentListService,映射路径 /StudentList
DemoDb里添加 save() 与 load() 方法
数据实际存储位置 c:\webdata\ 目录,使用JSON格式存储
1.2 前台
新建list.html
包含 js/jquery.min.js 和 js / afquery.js
使用 Af.rest () 来调用 RESTful 接口 (Af.rest() 在 afquery.js 里定义)
使用 AfTemplate来处理数据 ( AfTemplate在 afquery.js里定义)
( AfTemplate 的使用说明在本文档的第三部分)
二、增加记录
2.1 后台
新建 StudentAddService ,映射到 /StudentAdd
保存数据到 DemoDb
2.2 前台
新建 add.html
调用 Af.rest() 与后台交互
三、AfTemplate 使用说明
为了方便格式化HTML,所以提供一个AfTemplate 工具,在afquery.js 里定义。
示例,
var html = "<label target='{#id}'> {#title} </label>";
var tp = new AfTemplate(html);
var data = {
id: 1234,
title: '阿发你好',
};
var output = tp.replace(data);
console.log(output);
则输出为:
<label target='1234'> 阿发你好 </label>
其中,html为模板,data为数据
AfTemplate负责把模板中的变量{#VVV} 替代成数据的值( html + data => output)
9.2 学生管理-删除
一、 删除记录
1 后台
修改 DemoDb : 增加 remove(id) 方法,用于删除一个学生的记录
新增 StudentRemoveService : 映射到 /StudentRemove
2 前台
修改 list.html :
(1) 表格增加一列
<td>
<button data='{#id}' onclick='doRemove(this)'> 删除 </button>
</td>
(2) 点删除按钮时,删除本行
function doRemove ( e )
{
}
代码:
添加一个新的 webservlet.
@WebServlet("/StudentRemove")
public class StudentRemoveService extends AfRestfulService
{
@Override
protected Object execute(HttpServletRequest httpReq, HttpServletResponse httpResp, JSONObject jreq,
HashMap<String, String> queryParams) throws Exception
{
int id = jreq.getInt("id");
if( DemoDb.i.remove(id))
{
DemoDb.i.save(); // 数据有变化时,调用 save()同步到文件
}
return null;
}
}
在list页面添加删除按钮的方法.
//e:DOM元素对象,即被点击的按钮
function doRemove(e)
{
var req={};
req.id=$(e).attr("data"); //取得'data'属性
Af.rest("StudentRemove", req, function(data) {
//删除该行
$(e).parent().parent().remove();
alert("删除成功");
})
}
// DemoDB里实现按学号删除
public boolean remove(int id)
{
Iterator<Student> iter = data.iterator();
while(iter.hasNext())
{
Student s = iter.next();
if(s.getId() == id)
{
iter.remove();
return true;
}
}
return false;
}
9.3 查询功能.
写一个webservlet
@WebServlet("/StudentQuery")
public class StudentQueryService extends AfRestfulService
{
@Override
protected Object execute(HttpServletRequest httpReq,
HttpServletResponse httpResp, JSONObject jreq,
HashMap<String, String> queryParams) throws Exception
{
String name =jreq.getString("filter");
List<Student> rows = DemoDb.i.list(name);
JSONArray result =new JSONArray(rows);
return result;
}
}
写一个query方法
function query()
{
// 请求参数
var req = {};
req.filter =$('.toolbar .filter').val().trim();
Af.rest("StudentQuery", req, function(data){
// 后台传回来的 error , reason 在 afquery.js 里已经解出
// 这里只需要处理 data
showResult(data);
});
}
查询方法
// 按名字查询
public List<Student> list(String name)
{
List<Student> result = new ArrayList<Student>();
for(Student s: data)
{
if(s.name.indexOf( name ) >=0 )
{
result.add( s );
}
}
return result;
}
10.1 XML方式配置
Servlet的配置方式有两种:
-注解方式@WebServlet("/Example")
-XML方式(web.xml)
新建一个webproject,选择jdk1.8+tomcat8.0
然后next,next勾选Generate web.xml deployment descriptor
在webroot下面的web-inf下面有一个web.xml
web.xml有source和design两种模式,source是源码模式
<servlet>
<servlet-name>Example1</servlet-name>
<servlet-class>my.Example1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>Example1</servlet-name>
<url-pattern>/Example1</url-pattern>
</servlet-mapping>
加载过程
XML方式:
-tomcat启动后
-加载WEB-INF\web.xml里的Servlet
注解方式:
-tomcat启动
-扫描WEB-INF\classes下的Servlet
xml方式:可读性高
注解方式:操作简单
web.xml是一个必须掌握的配置文件
<welcome-file>:指定默认页
即http://127.0.0.1:8080/demol/ URL里不指下文件名时默认加载的文件
web.xml时应该保持存在的,因为以后还需要添加其他配置
10.2 Serlvet实例
现在有一个Servlet:
"/Example1"->my.Example1
考虑:当用户多次访问该服务时,后台创建了几个Example1对象?
验证
在my.Example1的构造方法里添加打印构造
观察:
-当tomcat启动时,会立刻创建Example1对象?
-当用户访问该服务时.
-当用户再次访问该服务时.
Servlet实例
Tomcat启动时,会创建一个全局的列表
servletConfigList
{
"/Example1","my.Example1",servlet(null)
"/Example2","my.Example2",servlet(null)
...
}
默认在启动时,不会立即创建Servlet实例
当Tomcat接收到访问时:
1.检查URI
2.找到servletConfigList,创建实例
servlet = new my.Example1()
3.处理请求
servlet.doGet()/doPost()
当客户端再次访问该URL时,会怎么处理.
随应用启动
一个WEB项目称为Web Application
可以配置Servlet在应用启动时实例化
<servlet>
<servlet-name>Example1</servlet-name>
<servlet-class>my.Example1</servlet-class>
<load-on-startup>1</load-on-startup> //添加load-on-startup
</servlet>
<servlet-mapping>
<servlet-name>Example1</servlet-name>
<url-pattern>/Example1</url-pattern>
</servlet-mapping>
或者@WebServlet(name="Example2",urlPatterns="/Example2",loadOnStartup=1)
规则:
1.<load-on-startup>表示,当该应用被启动时,自动创建Servlet实例,其数字表示启动顺序.
2.当存在多个自启动Servlet时,数字越小越优先启动.
3.如果没有<load-on-startup>配置,则默认不会创建实例,直到第一次被访问时才创建实例.
-默认情况下,第一次访问才创建
-该实例不会销毁,直到应用关闭
-可以配置成一启动就创建.
另外知道了怎么检查一个类是单例还是多例
10.3 Servlet生命周期
Lifecycle
指一个对象何时被创建,何时被销毁
生命周期的回调
init():在创建对象后,调用此方法进行初始化
-----------
doGet():当用户以GET方式访问此服务时调用
doPost():当用户以POST方式访问此服务时调用
-----------
destroy():应用退出前,调用此方法做善后处理
10.4 Tomcat请求处理流程
并发访问:指多路客户端同时访问同一台服务器
Tomcat服务器支持多路并发访问
多个用户可以同时访问一个Tomcat服务器
当接收到一个请求时,会创建一个线程来处理该请求
new Thread(){ //以下为伪代码
run(){
if(匹配servlet路径)servlet.doGet/doPost()
else if(匹配本地文件)读取文件内容发送给客户端
else "404 Not Found"
}
}
注意
单凭一个路径,不能判定是Servlet还是文件
例如:/images/1230.jpg 看上去是一个文件,但也可能是一个服务
/SimpleQuery 它看上去是一个服务,但也可能是只是一个文件
小结
提到了多线程处理的概念,每一个请求都在各自的线程里处理的.
第十一章
11.1 线程重入
指多个线程同时运行同一对象的同一方法.
对于Servlet来说,要求doPost()/doGet()可重入.
比如:/Example1=>my.Example1
当多个客户端同时访问/Example1时
-找到匹配的Servlet对象s
-创建多个线程
-每个线程里都调用s.doPost()方法
显示,此时要求doPost()方法可以重入
不可重入最简单的原则
简单原则:在Servlet的所有方法里,均不使用类的属性
很简单,如果不使用属性,就不会产生重入的问题.
不把数据存到属性里,直接传递调用.
11.2 单例多例转换
单例转多例,时一种更简单的办法,可以保证doPost()是可以重入的.
@WebServlet("/Example2")
public class Example2 extends HttpServlet
{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
new ServiceHandler(request,response).handle();
}
}
public class ServiceHandler
{
HttpServletRequest request;
HttpServletResponse response;
public ServiceHandler(HttpServletRequest request,HttpServletResponse response)
{
this.request =request;
this.response=response;
}
public void handle()throws ServletException, IOException
{
}
}
Example2:单例,仅一个实例
ServiceHandler:多例,每个线程创建一个实例
单例转多例的设计方法,
把所有的处理放在多例ServiceHandler里,就可以即好,又简单的避免"不可重入"问题
在ServiceHandler类里,可以随意使用自己的属性,不会产生问题.
12.1 本地文件
web项目里,文件的路径可以指定绝对路径,也可以指定相对路径
绝对路径:例如 C:/webdata/student.json
相对路径.
示例:实现一个功能,在页面输入标题和内容后将数据保存到文件.
-保存到data\目录下
-每次根据标题创建一个文件
创建Sava.html,前端页面,实现sava function
<body>
<div class='main'>
<div>
<input type='text' class='title' />
</div>
<br>
<div>
<textarea class='content' style="height:100px; width:200px;"></textarea>
</div>
<div>
<button onclick="save()">save</button>
</div>
</div>
</body>
<script>
window.save=function()
{
var req={};
req.title=$('.title').val().trim();
req.content=$('.content').val().trim();
Af.rest("SaveContent",req,function(data){
alert("保存成功");
$(".main input").val("");
$(".main textarea").val("");
})
}
</script>
创建一个Servlet SaveContent实现存储
@WebServlet("/SaveContent")
public class ServletSaveContent extends AfRestfulService
{
@Override
protected Object execute(HttpServletRequest httpReq,
HttpServletResponse httpResp, JSONObject jreq,
HashMap<String, String> queryParams) throws Exception
{
//取得参数
String title =jreq.getString("title");
String content=jreq.getString("content");
//获取项目实际路径
String webrootPath =httpReq.getServletContext().getRealPath("/");
File webroot = new File(webrootPath);
//存储到$(webroot)/data/xxx.txt
File dataDir=new File(webroot,"data/");
dataDir.mkdirs();
File f=new File(dataDir,title+".txt");
AfTextFile.write(f, content, "UTF-8");
return null;
}
}
可以在代码获取当前项目的部署路径
使用httpReq.getServletContext().getRealPath("/"); //path路径
注:web项目没有"当前目录"的概念.
12.2 资源文件
在Java项目里,资源文件是指存放在Classpath里的文件
非源码文件就称为资源文件resource(资源)
演示:在src下的xml文件,会被MyEclipse自动拷贝到classes目录下
(注:也可以使用jpg txt等文件类型试验)
资源文件路径:和类路径类似
例如:/config.xml /my/example/logo.png
在项目里添加一个配置文件/course.xml 配置有课程的信息.
InputStream stream = getClass().getResourceAsStream("/course.xml")
SAXReader reader =new SAXReader();
Document doc =reader.read(stream);
stream.close();
可以在程序里访问资源文件
InputStream stream = getClass().getResourceAsStream("/course.xml")
-资源文件是只读的
-资源文件路径和类路径写法相同
-程序实际访问不是src下的文件,而是classes下的文件.
第十三章 通用服务框架
13.1通用的Web服务框架,使用这个框架可以很快捷的添加web服务.
添加AfGenericService,并在web.xml里添加配置
注意:
(1)URI匹配规则:*.api表示所有后缀为*.api的请求都由这个Servlet处理
(2)<load-on-startup>表示当应用被加载时就创建Servlet实例
<servlet>
<servlet-name>AfGenericService</servlet-name>
<servlet-class>af.web.service.AfGenericService</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>AfGenericService</servlet-name>
<url-pattern>*.api</url-pattern>
</servlet-mapping>
http://127.0.0.1:8080/demo/de1.api任意以.api结尾的都由AfGenericService处理.
public class AfGenericService extends HttpServlet
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
// 处理请求数据
try{
handleRequest(request, response);
}
catch(Exception e)
{
e.printStackTrace();
response.sendError(500, e.getMessage());
return;
}
}
private void handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception
{
// 从URL中解析API的名字
// servletPath: "/.../hello.api"
String servletPath = request.getServletPath();
int p1 = servletPath.lastIndexOf('/');
int p2 = servletPath.lastIndexOf('.');
String apiName = servletPath.substring(p1 + 1, p2); //substring(a,b)从a开始取直到b之前
System.out.println("服务名:" + apiName);
// 发送应答给客户端
response.setCharacterEncoding("UTF-8");
response.setContentType("text/plain");
//response.setHeader("Connection", "close");
Writer writer = response.getWriter();
writer.write( "OK,收到您的请求 :" + apiName );
writer.close();
}
}
13.2 通用服务框架2
单例转多例的设计方法,根据不同的服务名,创建不同得到对象来处理.
对应关系:
Hello.api->my.HelloApi
HowOld.api->my.HowOldApi
AreYouGood.api->my.AreYourGood.api
// 根据服务名, 创建不同的实例进行处理 ( 单例 -> 多例 )
AfGenericApi instance = null;
if("Hello".equals(apiName))
instance = new HelloApi();
else if("HowOld".equals(apiName))
instance = new HowOldApi();
else if("AreYouGood".equals(apiName))
instance = new AreYouGoodApi();
else if("Time".equals(apiName))
instance = new WhatTimeNowApi();
else
throw new Exception("不支持这个服务: " + apiName);
// 读取请求数据 和 URL里的参数
String charset = "UTF-8";
String strReq = AfServiceUtils.readAsText(request.getInputStream(), charset, MAX_REQUEST_SIZE);
String query = request.getQueryString();
HashMap<String,String> queryParams = AfServiceUtils.parseQuery(query, charset);
// 读取请求数据, 转成字符串, 转成 JSON
instance.httpReq = request;
instance.httpResp = response;
instance.queryParams = queryParams;
instance.charset = charset;
String strResp = instance.execute(strReq); // 具体的请求处理在execute()里
api继承于抽象类AfGenericApi
在各个api中重写execute().
13.3 通用服务框架3
使用反射技术改造框架
创建xml,new file,选择目录src下面创建af-service.xml 右键properties other UTF-8
用af-service.xml来描述对应关系:
Hello.api->my.HelloApi
HowOld.api->my.HowOldApi
AreYouGood.api->my.AreYourGood.api
af-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<config>
<service name="Hello" class="my.HelloApi" />
<service name="HowOld" class="my.HowOldApi" />
<service name="AreYouGood" class="my.AreYouGoodApi" />
</config>
然后在AfGenericService中重写init()方法. //init():在创建对象后,调用此方法进行初始化
@Override
public void init() throws ServletException
{
//从xml配置文件中读取配置
try
{
loadConfig();
}catch(Exception e)
{
e.printStackTrace();
throw new Error("af-service.xml 格式不正确!启动终止启动!");
}
}
再添加一个loadConfig()方法来实现读取
// 从 af-service.xml 中获取配置
private void loadConfig() throws Exception
{
InputStream stream = this.getClass().getResourceAsStream(
"/af-service.xml");
SAXReader reader = new SAXReader();
Document doc = reader.read(stream);
stream.close();
Element root = doc.getRootElement();
List<Element> xServiceList = root.elements("service");
for (Element e : xServiceList)
{
String name = e.attributeValue("name");
String clazzName = e.attributeValue("class");
configs.put(name, new ConfigItem(name, clazzName));
}
protected HashMap<String, ConfigItem> configs = new HashMap<String, ConfigItem>();
af-config.xml中的配置项
// af-service.xml 中的配置项
class ConfigItem
{
public String name; // 服务接口名
public String clazzName; // 类名
public Class clazz; // 类的实体
public String charset = "UTF-8";
public ConfigItem(String name, String clazzName)
{
this.name = name;
this.clazzName = clazzName;
}
}
框架代码和业务代码分离
在af.web.service.*里不再包含my.*里的类
13.4 通用服务框架4 //AfRestfulApi代码未看.
基本通用服务框架,来实现RESTful接口:
RESTful的特点:请求JSON,应用JSON
所以,建立一个AfRestfulApi作为基类统一处理
添加RESTful接口
步骤:
1.创建一个类,继承于AfRestfulApi
2.在af-service.xml里添加描述
13.5 通用服务框架的使用
如何在一个新的web项目下使用af-web-1.0.jar
13.6使用afservice框架改造9.4学生管理系统
afweb.jar里不仅包含afservice,还将包含其他框架.
14.1 创建JSP
JSP, Java Server Page
Java服务端页面,一种前端和后端混合的开发方式.
历史上曾经流行的JSP,PHP,ASP三大服务端页面之一.
新建JSP页面
在WebRoot目录下,右键,新建一个JSP页面
观察JSP的内容形式:
-整体上还是HTML
-部分以<% %>引起来的内容,时特殊代码.
<%@page import="java.text.SimpleDateFormat"%>
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<base href="<%=basePath%>">
<title>My JSP 'showtime.jsp' starting page</title>
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="expires" content="0">
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="This is my page">
<!--
<link rel="stylesheet" type="text/css" href="styles.css">
-->
</head>
<body>
<%
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String timestr =sdf.format(System.currentTimeMillis());
out.write(timestr); //PrintWriter
%>
This is my JSP page. <br>
</body>
</html>
JSP是一种在HTML中嵌入JAVA代码的页面
<% %>中间的是特殊代码
14.2 JSP运行原理
1.tomcat会把每一个JSP文件自动转换为Java类
(观察tomcat\work\目录下的生成物)
2.客户端请求*.jsp时,由*.class进行处理
所以,JSP本质上就是Servlet
JSP所有的规则,都是为了方便转换为JAVA代码
例如:
<%@ %>用于设置页面的参数
<% %>直接代码
<%= %>变量引用
上下文对象:request ,response, out
JSP的缺陷
开发效率问题:
前端代码和后端代码混杂,不利于开发管理
运行效率问题:
生成Java代码运行效率低.
JSP技术只能是一种历史.
14.3 JSP代码分离
<%@ %>用于设置页面的参数(放在页面顶部)
<%@ page language="java" import="java.util.*" pageEncoding="ISO-8859-1"%>
language语言,import导入的包,pageEncoding字符集格式,如果需要使用中文应该使用"UTF-8"
<% %>直接代码(应杜绝使用)
<%= %>变量引用
上下文对象:request ,response, out
<jsp:include page="xxx.jsp" /> 包含另一个页面
如果一定要使用JSP,那么java代码尽量不要写在JSP里面,减少HTML和java代码的混杂
1.新建一个Java类
my.jsp.JspSupport //Support支持
public class JspSupport
{
HttpServletRequest request;
public JspSupport(HttpServletRequest request)
{
this.request = request;
}
public JSONArray getList()
{
List<Student> rows = DemoDb.i.list();
return new JSONArray(rows);
}
}
把相关的处理放在Java类里。例如,添加一个 getList() 方法,返回所有学生的数据。
2 在JSP里引用
先在顶上指定字符集,并导入 my.jsp.*
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%@ page import="my.jsp.*"%>
然后
var data = <%= new JspSupport(request).getList() %>;
这样,就在最大程度上对 HTML 与 Java代码进行了分离。
介绍了JSP代码分离的实现方法
把业务代码放在Java类里,添加方法:
-输入参数request,response
-返回值JSONObject/JSONArray
这样就可以最大程度上分离了HTML和Java代码.
第15章
15.1 JavaWeb和JDBC
JavaWeb:用于Java实现的网站后台
JDBC:用Java实现的与数据库交互
一般情况下,网站的数据要存储在数据库里
在JavaWeb里使用JDBC技术,是自然而然的事情.
一般网站的构架
客户端浏览器1------->互
客户端浏览器2------->联------>Web服务器(Tomcat)<---->数据库(MySQL)
客户端浏览器3------->网
15.2 数据库支持(afsql)
一,数据库的设计
导入数据库 af_school.sql
二,在Web项目中添加数据库支持
1.在WEB-INF\lib目录下添加jar包
2.在src目录下添加c3p0-config.xml
并修改里面的数据库配置:数据库IP,数据库名,用户名,密码等
3.新建packge,添加C3P0Factory
4.生成POJO类
打开POJO生成器,生成POJO类,把生成的Java拷贝到项目里.
15.3 数据库测试
写一个TestDb.java
package my.dbutil;
import java.util.List;
import my.db.*;
public class TestDb
{
public static void listStudent()throws Exception
{
String sql="select * from student";
List<Student> rows =C3P0Factory.executeQuery(sql, Student.class);
System.out.println("获取到:" + rows.size());
}
public static void addStudnet()throws Exception
{
Student s =new Student();
s.setId(20180100);
s.setName("xxxx");
s.setSex(false);
s.setPhone("13996000000");
C3P0Factory.insert(s);
}
public static void main(String[] args)
{
try
{
addStudent();
listStudent();
}catch(Exception e)
{
e.printStackTrace();
}
}
}
获取到:12
15.4 RESTful框架支持
-WEB-INF\lib
-web.xml
-af-service.xml
-添加API...
15.5 网站的一般架构
一般网站的构架
客户端浏览器1------->互
客户端浏览器2------->联------>Web服务器(Tomcat)<---->数据库(MySQL)
客户端浏览器3------->网 公网服务器
客户端:浏览器,app等.
客户端为什么不直接访问数据库服务器
16.1 文件的上传
文件上传:即通过网页的方式,打开文件对话框,选择文件并上传到服务器
比如:发生email时选择附件,修改用户头像等..
前端:使用<form>
后端:使用Servlet接收文件数据
(借助apache commons工具类解析请求数据)
一,前端
//encode type
<form method="post" action="FileUploadService" enctype="multipart/form-data">
<input type="file" name="fileUpload" /> <br>
<input type="submit" value="上传" /> <br>
</form>
注意:<form> 标签要添加一个属性 enctype
<input type="submit" /> 定义提交按钮。提交按钮用于向服务器发送表单数据。数据会发送到表单的 action 属性中指定的页面。
<input type="file" /> 用于文件上传。
<input type="file"/>文件选择按钮,用于打开文件对话框
当点submit按钮时,浏览器会自行将文件传给后台
二,后端
1.添加commons库的支持
commons-fileupload-1.3.1.jar
commons-io-2.4.jar
都是apache.org提供的基础开发包,免费开源.
2.添加FileUploadService
小结:
1.浏览器会把文件的数据传递到后台.
数据的格式是HTTP POST multi-part
2.后台创建Servlet,处理POST请求
使用apache commons fileupload 的API来解析请求数据,存储到本地文件里
16.2 JS文件上传
使用JavaScript可以实现文件上传
(不再使用<form>标签)
优点:
-可以显示上传进度,可以取消上传
-界面更加灵活,美观.
后台代码不变,添加upload2.html.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title></title>
<script type="text/javascript" src="js/jquery.min.js" ></script>
<script type="text/javascript" src="js/afquery.js" ></script>
<style>
.main{
}
.main input{
margin: 20px;
}
.main .upload{
background-color: cornflowerblue;
color: #fff;
border: 1px solid #ccc;
border-radius: 1px;
padding: 8px 18px;
}
</style>
</head>
<body>
<div class='main'>
<input type='file' class='filebutton' style='display:none' onchange='fileSelected()' /> <br>
<button class="upload" onclick='openFileDialog()' > 选择文件上传 </button>
</div>
</body>
<script>
// 点击普通按钮
function openFileDialog()
{
// 模拟触发点击事件
$('.filebutton').click();
}
// 用户选择了一个文件 onchange事件被触发
function fileSelected()
{
var fbutton = $('.filebutton')[0]; // DOM
var file = fbutton.files[0]; // fbutton.files 可能一次选了多个文件
fbutton.value = ''; // 清空选择
startUpload ( file ); // 开始上传
}
// 开始上传, 参数为 File 对象
function startUpload( file )
{
var uploadUrl = "FileUploadService";
// 手工构造一个 form 对象
var formData = new FormData();
formData.append('file', file); // 'file' 为HTTP Post里的字段名, file 对浏览器里的File对象
// 手工构造一个请求对象,用这个对象来发送表单数据
// 设置 progress, load, error, abort 4个事件处理器
var request = new XMLHttpRequest();
request.upload.addEventListener("progress", window.evt_upload_progress, false);
request.addEventListener("load", window.evt_upload_complete, false);
request.addEventListener("error", window.evt_upload_failed, false);
request.addEventListener("abort", window.evt_upload_cancel, false);
request.open("POST", uploadUrl ); // 设置服务URL
request.send(formData); // 发送表单数据
}
window.evt_upload_progress = function (evt)
{
if (evt.lengthComputable)
{
var progress = Math.round(evt.loaded * 100 / evt.total);
Af.log ("上传进度: " + progress);
}
};
window.evt_upload_complete = function (evt)
{
if(evt.loaded == 0)
{
Af.log ("上传失败!");
}
else
{
Af.log ("上传完成!");
var response = JSON.parse(evt.target.responseText);
Af.log (response);
}
};
window.evt_upload_failed = function (evt)
{
Af.log ("上传出错");
};
window.evt_upload_cancel = function (evt)
{
Af.log( "上传中止!");
};
</script>
</html>
styple='display:none' 隐藏
onchange='fileSelcted()'表示点击时触发
小结:
学习了使用JS控制文件上传的原理
在这个原理的基础上,可以灵活的实现多种形式的上传.比如,多个文件同时上传...