HTML5提供了功能强大的Web通信机制,可以实现不同域的Web应用程序之间的安全通信,也可以在JavaScript上进行HTTP(S)通信和WebSocket通信。这些都是构建桌面式Web应用的基础。
1、跨文档消息机制
在HTML4中,出于安全考虑,一般不允许一个浏览器的不同框架、不同标签页、不同窗口之间的应用程序互相通信,以防止恶意攻击。但在实际应用中,有时需要进行跨文档通信,例如,在一个窗口发送一个消息到另一个窗口以实现聊天的功能。HTML5提供了这种跨文档通信的消息机制。
1)检测浏览器对跨文档消息机制的支持情况
在JavaScript中可以使用window.postMessage属性检测浏览器对跨文档消息机制的支持情况。如果typeof window.postMessage等于undefined,则表明当前浏览器不支持跨文档消息机制;否则表明支持。
在网页中定义一个按钮,单击此按钮时,会检测浏览器是否支持跨文档消息机制。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<button id="check" onclick="check();">检测浏览器是否支持跨文档消息机制</button>
<script>
function check() {
if (typeof window.postMessage == "undefined") {
alert("您的浏览器不支持跨文档消息机制。");
} else {
alert("您的浏览器支持跨文档消息机制。");
}
}
</script>
</body>
</html>
浏览器 | 对跨文档消息机制的支持情况 |
---|---|
Chrome | 2.0及以后的版本支持 |
Firefox | 3.0及以后的版本支持 |
Internet Explorer | 8.0及以后的版本支持 |
Opera | 9.6及以后的版本支持 |
Safari | 4.0及以后的版本支持 |
2)使用postMessage API 发送消息
可以调用postMessage API 实现跨文档发送消息,语法如下。
window.postMessage(data, url)
参数说明如下。
- data:发送消息中包含的数据,通常是一个字符串。
- url:指定允许通信的域名。注意,不是接受消息的目标域名。使用该参数的主要作用是出于安全的考虑,接收消息的窗口可以根据此参数判断消息是否来自可信任的来源,以避免恶意攻击。如果不判断访问的域,则可以使用“'*'”。
window是接收消息的窗口对象,例如,向父窗口发送消息可以使用 window.parent.postMessage(),如果页面中包含两个框架,则可以使用下面的代码向第2个框架发送消息。
window.parent.frames[1].postMessage(message, '*');
演示跨框架发送消息。
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<frameset framespacing="1" border="1" bordercolor="#333399" frameborder="yes" />
<frameset cols="500, *">
<frame name="left" target="main" src="html/a.html" scrolling="auto" frameborder="1">
<frame name="main" src="html/b.html" scrolling="auto" noresize frameborder="1">
</frameset>
<body>
<p>此网页使用了框架,但您的浏览器不支持框架。</p>
<script>
</script>
</body>
</html>
a.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
mt10 {
margin-top: 10px;
}
</style>
</head>
<body>
<form>
<p><input type="text" required autofocus /></p>
<p class="mt10">
<input type="submit" value="确认" />
</p>
</form>
<script>
var eleForm = document.querySelector("form");
eleForm.onsubmit = function() {
var message = document.querySelector("input[type='text']").value;
window.parent.frames[1].postMessage(message, '*');
return false;
}
</script>
</body>
</html>
b.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
.p20 {
padding: 20px;
}
</style>
</head>
<body>
<div id="message" class="p20">
尚未接收到消息
</div>
<script>
window.addEventListener('message', function (e) {
document.getElementById('message').innerHTML = e.data;
});
</script>
</body>
</html>
3)监听和处理消息事件
在接收消息的窗口中,需要监听postMessage()方法发送的消息事件,并处理其中包含的数据。
如果使用addEventListener()方法指定事件的处理函数,则需要指定监听事件为message,方法如下。
window.addEventListener("message", messageHandle, false);
如果使用attachEvent()方法指定事件的处理函数,则需要指定监听事件为onmessage,方法如下。
window.attachEvent('onmessage', messageHandle);
messageHandle是事件的处理函数,格式如下。
var messageHandle = function(e) {
...
}
参数e有两个属性,即e.data 和 e.origin。e.data 是接收到的数据,也就是postMessage()方法的第1个参数指定的数据;e.origin是传送源,也就是postMessage()方法的第2个参数指定的数据。如果出于安全考虑,只处理来自指定传送源的消息,则可以在处理函数messageHandle中做如下处理。
var messageHandle = function(e) {
switch (e.origin) { // 获取传送源
case "safeorigin" : // 信任源的处理,可以将"safeorigin"替换为任意标记信任源字符串,
// 即postMessage()方法的第2个参数指定的数据
var data = e.data; // 获取数据处理
break;
default :
// 不信任源的处理
......
}
}
如果在postMessage()方法的第2个参数中指定的数据为“'*'”,则处理函数messageHandle接收到消息时,e.origin为“"undefined"”。
将之前的代码补充完成,增加监听和处理事件的代码,代码如下。
<script>
var eleBox = document.querySelector("#message");
var messageHandle = function(e) {
eleBox.innerHTML = '接收到的信息是:' + e.data + "<br/>" + e.origin;
};
if (window.addEventListener) {
window.addEventListener("message", messageHandle, false);
} else if (window.attachEvent) {
window.attachEvent('onmessage', messageHandle);
}
</script>
在传统Web应用中,表单将数据提交到Web服务器,经过处理后再将数据传送回浏览器显示。跨文档消息机制无需Web服务器的参与就可以将数据传送到指定的框架或窗口,也不需要刷新窗口内容,即可显示接收到的数据。这无疑增强了Web应用的桌面处理的能力。
2、XMLHttpRequest Level 2
XMLHttpRequest 是一个浏览器接口,开发者可以使用它提出 HTTP 和 HTTPS 请求,而且不用刷新页面就可以修改页面的内容。XMLHttpRequest 的两个最常见的应用是提交表单和获取额外的内容。
使用 XMLHttpRequest 对象可以实现下面的功能。
- 在不重新加载页面的情况下更新网页。
- 在页面已加载后从服务器请求数据。
- 在页面已加载后从服务器接收数据。
- 在后台向服务器发送数据。
早期的 XMLHttpRequest 只能请求文本、HTML 和 XML,发送有语法格式的、读写都很复杂的变量和值,而且早期的XMLHttpRequest 遵循同域的原则,这使得跨域请求变得很复杂。例如,没有中介(如代理服务器)就不能在 http://foo.example/ 和 http://bar.example 实现数据共享。
HTML5中包含了 XMLHttpRequest 的一个新版本——XMLHttpRequest Level 2。下面我们就介绍使用 XMLHttpRequest Level 2 进行Web通信的具体方法。
1)创建XMLHttpRequest对象
对于不同的浏览器,创建XMLHttpRequest对象的代码也可能不同。
(1)微软公司的IE浏览器
在微软公司的IE浏览器中使用Active对象创建XMLHttpRequest对象,代码如下。
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
当window.ActiveXObject等于True时,可以使用这种方法。
(2)其他浏览器
在其他浏览器中可以用下面的代码创建XMLHttpRequest对象。
xmlhttp = new XMLHttpRequest();
当window.XMLHttpRequest等于True时,可以使用这种方法。
(3)通用的创建XMLHttpRequest对象的代码
综上所述,可以在各种浏览器中创建XMLHttpRequest对象的代码如下。
var xmlHttp;
if (window.XMLHttpRequest) {
xmlHttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
2)发送HTTP请求
在发送HTTP请求之前,需要调用open()方法初始化HTTP请求的参数,语法如下。
open(method, url, async, username, password)
参数说明如下。
- method:用于请求的HTTP方法,值包括GET、POST和HEAD。
- url:所调用的服务器资源的URL。
- async:布尔值,指示这个调用使用异步还是同步,默认为true(即异步)。
- username:可选参数,为URL所需的授权提供认证用户。
- password:可选参数,为URL所需的授权提供认证密码。
例如,使用GET方法以异步形式请求访问URL的代码如下。
xmlhttp.open("GET", url, true);
open()方法只是初始化HTTP请求的参数,并不真正发送HTTP请求。可以调用send()方法发送HTTP请求,语法如下。
send(body)
如果调用open()方法指定的HTTP方法是POST或GET,则body参数指定了请求体,它可以是一个字符串或者Document对象。如果不需要指定请求体,则可以将这个参数设置为null。
send()方法发送的HTTP请求通常由以下几部分组成。
- 之前调用open()时指定的HTTP方法、URL以及认证资格(如果有的话)。
- 如果之前调用setRequestHeader()方法发送了HTTP请求的头部,则包含指定的请求头部。
- 传递给这个方法的body参数。
当XMLHttpRequest对象把一个HTTP请求发送到服务器时将经历若干种状态,XMLHttpRequest对象的ReadyState属性可以表示请求的状态,它的取值如下所示。
值 | 具体说明 |
---|---|
0 | 表示已经创建一个XMLHttpRequest对象,但是还没有初始化,即还没调用open()方法 |
1 | 表示正在加载,此时对象已建立,已经调用open()方法,但还没调用send()方法 |
2 | 表示请求已发送,即方法已调用send(),但服务器还没有响应 |
3 | 表示请求处理中。此时,已经接收到HTTP响应头部信息,但是消息体部分还没有完全结束接收 |
4 | 表示请求已完成,即数据接收完毕,服务器的响应完成 |
3)从服务器接收数据
发送HTTP请求之后,就要准备从服务器接收数据了。首先要指定响应处理函数。定义相应处理函数后,将函数名赋值给XMLHttpRequest对象的onreadystatechange属性即可。例如:
xmlHttp.onreadystatechange = callBack
// 指定响应函数
function callBack() {
// 函数体
......
}
提示:响应处理函数没有参数,指定时也不带括号。
也可以不定义响应处理函数的函数名,直接定义函数体。例如:
request.onreadystatechange = function() {
// 函数体
......
}
当readyState属性值发生改变时,XMLHttpRequest对象会激发一个readystatechange事件,此时会调用响应处理函数。
在响应处理函数中通常会根据XMLHttpRequest对象的ReadyState属性和其他属性决定对接收数据的处理。除了ReadyState属性外,XMLHttpRequest的常用属性如下所示。
值 | 具体说明 |
---|---|
responseText | 包含客户端接收到的HTTP响应的文本内容。当readyState值为0、1或2时,responseText属性为一个空字符串。当readyState值为3时,responseText属性为还未完成的响应信息。当readyState为4时,responseText属性为响应的信息 |
responseXML | 用于当接收到完整的HTTP响应时(readyState为4)描述XML响应。如果readyState值不为4,那么responseXML的值也为null |
status | 用于描述HTTP状态代码,其类型为short。仅当readyState值为3或4时,status属性才可用 |
statusText | 用于描述HTTP状态代码文本。仅当readyState值为3或4时才可用 |
常用的响应处理函数框架如下。
function callBack() {
if (request.readyState == 4) { // 服务器已经响应
if (request.status == 200) { // 请求成功
// 显示服务器响应
......
}
}
}
request.status等于200表示请求成功。
在网页中定义一个按钮,单击此按钮时,使用XMLHttpRequest对象从服务器获取并显示一个XML文件的内容。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<p>
<b>Status:</b>
<span id="A1"></span>
<!-- A1用于显示status属性值 -->
</p>
<p>
<b>statusText:</b>
<span id="A2"></span>
<!-- A2用于显示statusText属性值 -->
</p>
<p>
<b>responseText:</b>
<span id="A3"></span>
<!-- A3用于显示responseText属性值 -->
</p>
<button onclick="loadXMLDoc('example.xml')">获取XML文件</button>
<script>
function loadXMLDoc(url) {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp != null) {
xmlhttp.onreadystatechange = state_Change;
xmlhttp.open("GET", url, true);
xmlhttp.send(null);
} else {
alert("您的浏览器不支持XMLHTTP.");
}
}
function state_Change() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
document.getElementById('A1').innerHTML = xmlhttp.status;
document.getElementById('A2').innerHTML = xmlhttp.statusText;
document.getElementById('A3').innerHTML = xmlhttp.responseText;
} else {
alert("接收XML数据时出现问题:" + xmlhttp.statusText);
}
}
}
</script>
</body>
</html>
因为XMLHttpRequest是与Web服务器进行通信的接口,所以要查看上述代码的运行结果,就需要搭建一个Web服务器,可以使用IIS或Apache。搭建成功后,将上述代码和请求的example.xml复制到网站的根目录下。
4)进行HTTP头(HEAD)请求
在HTTP协议中,客户端从服务器获取某个网页时,必须发送一个HTTP的头文件,告诉服务器客户端要下载什么信息以及相关的参数。
XMLHttpRequest对象可以发送和获取HTTP头(HEAD)。使用抓包工具可以捕获HTTP头数据。
常见的HTTP头说明如下。
- Host:初始URL中的主机和端口。
- Connection:表示是否需要持久连接。如果值为Keep-Alive,或者请求使用的是HTTP1.1(HTTP1.1默认进行持久连接),就可以利用持久连接的优点,当页面包含多个元素(图片或多媒体等)时,显著减少下载需要的时间。
- User-Agent:浏览器类型。
- Accept:浏览器可接受的MIME类型。多功能Internet邮件扩充服务(Multipurpose Internet Mail Extensions, MIME)是一种多用途网际邮件扩充协议。MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问时,浏览器会自动使用指定应用程序来打开。多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式。常见的MIME类型包括超文本标记语言文本(.html、.html text/html)、普通文本(txt text/plain)、RTF文本(.rtf application/rtf)、GIF图形(.gif image/gif)、PEG图形(.ipeg、.jpg image/jpeg)、au声音文件(.au audio/basic)、MIDI音乐文件(mid、.midi audio/midi、audio/x-midi)、RealAudio音乐文件(.ra、.ram audio/x-pn-realaudio)、MPEG文件(.mpg、.mpeg video/mpeg)、AVI文件(.avi video/x-msvideo)等。
- Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。
- Accept-Encoding:浏览器能够进行解码的数据编码方式。
- Accept-Language:浏览器希望的语言种类。
- Accept-Charset:浏览器可接受的字符集。
- Last-Modified:文档的最后改动时间。
下面介绍使用XMLHttpRequest对象发送和获取HTTP头(HEAD)的方法。
(1)设置HTTP头
调用setRequestHeader()方法可以向Web服务器发送一个HTTP头的名称和值,从而设置HTTP头,语法如下。
setRequestHeader(name, value)
参数name是要设置的头部的名称。这个参数不应该包括空白、冒号或换行;参数value是头部的值。这个参数不应该包括换行。
提示:应在调用open()方法之后,在调用send()方法之前,调用setRequestHeader()方法。
例如,在采用POST提交方式模拟表单提交数据时,应该执行下面的语句。
xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
(2)进行头请求
可以调用getResponseHeader()方法从响应信息中获取指定的HTTP头,语法如下。
trValue = XMLHttpRequest.getResponseHeader(bstrHeader);
参数bstrHeader指定请求HTTP头名,方法返回请求的HTTP头的值。
在网页中定义一个按钮,单击此按钮时,使用XMLHttpRequest对象从服务器获取并显示一个XML文件的最后修改日期。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<p id="p1">
演示getResponseHeader()方法的使用。
</p>
<button onclick="loadXMLDoc('example.xml')">获取XML文件的最后修改日期</button>
<script>
function loadXMLDoc(url) {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp != null) {
xmlhttp.onreadystatechange = state_Change;
xmlhttp.open("GET", url, true);
xmlhttp.send(null);
} else {
alert("您的浏览器不支持XMLHTTP.");
}
}
function state_Change() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
document.getElementById('p1').innerHTML = "XML文件的最后修改日期:" + xmlhttp.getResponseHeader('Last-Modified');
} else {
alert("接收XML数据时出现问题:" + xmlhttp.statusText);
}
}
}
</script>
</body>
</html>
因为XMLHttpRequest是与Web服务器进行通信的接口,所以要查看上述代码的运行结果,就需要搭建一个Web服务器,可以使用IIS或Apache。搭建成功后,将上述代码和请求的example.xml复制到网站的根目录下。
也可以调用getAllResponseHeader()方法获取完整的HTTP响应头部,语法如下。
strValue = oXMLHttpRequest.getAllResponseHeaders();
getAllResponseHeaders()方法返回请求的完整HTTP头的值。
演示使用getAllResponseHeaders()方法获取完整的HTTP响应头部的方法。改进上面的代码,将处理函数修改如下。
function state_Change() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
document.getElementById('p1').innerHTML = "HTTP头:" + xmlhttp.getAllResponseHeaders();
} else {
alert("接收XML数据时出现问题:" + xmlhttp.statusText);
}
}
}
5)超时控制
与服务器通信有时很耗时,可能由于网络原因或服务器响应等因素导致用户长时间等待,而且等待时间是不可预知的。
XMLHttpRequest Level 2 中增加了timeout属性,可以设置HTTP请求的时限,单位为ms。例如:
xhr.timeout = 5000;
上面的语句将最长等待时间设为5000ms(5s)。超过了这个时限,系统就自动停止HTTP请求。还可以通过timeout事件来指定回调函数,例如:
xhr.ontimeout = function(event) {
alert('请求超时!');
}
6)使用FormData对象向服务器发送数据
在XMLHttpRequest Level 2 中可以使用FormData对象模拟表单向服务器发送数据。
(1)创建FormData对象
可以使用两种方法创建FormData对象,一种是使用new关键字创建,方法如下。
var formData = new FormData();
另一种方法是调用表单对象的getFormData()方法获取表单对象中的数据,方法如下。
var formElement = document.getElementById("myFormElement");
formData = formElement.getFormData();
(2)向FormData对象中添加数据
可以使用append()方法向FormData对象中添加数据,语法如下。
formData.append(key, value);
FormData对象中的数据是键值对格式的,参数key为数据的键,参数value是数据的值。例如:
formData.append('username', 'lee');
formData.append('num', 123);
(3)向服务器发送FormData对象
可以使用XMLHttpRequest对象的send()方法向服务器发送FormData对象,语法如下。
xmlhttp.send(formData);
在发送FormData对象之前,也需要调用open()方法设置提交数据的方式以及接收和处理数据的服务器端脚本。例如:
xmlhttp.open('POST', "ShowInfo.php");
(4)在服务器端接收和处理表单数据
XMLHttpRequest是一个浏览器窗口,它只工作在浏览器端,在服务器端通常由PHP、ASP等脚本语言接收和处理表单数据。这里以PHP为例,介绍在服务器端接收和处理表单数据的方法。
表单提交数据的方式可以分为GET和POST两种。在PHP程序中,可以使用HTTP GET变量$_GET读取使用GET方式提交的表单数据,具体方法如下。
参数值 = $_GET[参数名]
使用HTTP POST变量$_POST读取使用POST方式提交的表单数据,具体方法如下。
参数值 = $_POST[参数名]
演示使用FormData对象向服务器发送数据的方法。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<p>
<span id="A1"></span>
</p>
<button onclick="sendformdata();">发送数据</button>
<script>
var xmlhttp;
function sendformdata() {
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp != null) {
xmlhttp.onreadystatechange = state_Change;
var formData = new FormData();
formData.append('name', 'lee');
formData.append('age', 38);
xmlhttp.open("POST", "ShowInfo.php");
xmlhttp.send(formData);
} else {
alert("您的浏览器不支持XMLHTTP.");
}
}
function state_Change() {
if (xmlhttp.readyState == 4) {
if (xmlhttp.status == 200) {
document.getElementById('A1').innerHTML = xmlhttp.responseText;
} else {
alert("接收XML数据时出现问题:" + xmlhttp.statusText);
}
}
}
</script>
</body>
</html>
服务器端脚本为ShowInfo.php的代码如下。
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<?PHP
echo ("username:" . $_POST['name'] . "<br>");
echo ("age:" . $_POST['age'] . "<br>");
?>
将上述代码都上传到Apache网站的根目录,打开HTML文件,单击按钮即可看到运行效果。
7)使用FormData对象上传文件
可以使用append()方法向FormData对象中添加文件数据,语法如下。
formData.append(key, File对象);
参数key为数据的键, File对象是FileList数组的元素,用于代表用户选择的文件数据。例如,使用下面的input元素选择文件。
<input type="file" name="fileToUpload" id="fileToUpload" multiple="multiple" />
向FormData对象中添加文件数据的代码如下。
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUpload').files[0]);
与发送普通数据一样,可以使用XMLHttpRequest对象的send()方法向服务器发送包含文件数据的FormData对象。在发送FormData对象之前,也需要调用open()方法设置提交数据的方式以及接收和处理数据的服务器端脚本。
XMLHttpRequest Level 2 还提供了一组与传送数据相关的事件,如下所示。
事件 | 具体说明 |
---|---|
progress | 在传送数据的过程中会定期触发,用于返回传送数据的进度信息。在progress事件的处理函数中可以使用该事件的属性计算并显示传送数据的百分比。progress事件的属性如下。
|
load | 传送数据成功完成 |
abort | 传送数据被中断 |
error | 传送过程中出现错误 |
loadstart | 开始传送数据 |
使用FormData对象实现可以显示进度的文件上传。
1、上传文件的网页设计
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload</title>
</head>
<body>
<form id="form1" enctype="multipart/form-data">
<h1 align="center">上传文件的演示实例</h1>
<p align="center">选择上传的文件</p>
<table width="80%" border="0" align="center">
<tr><td align="center"><input type="file" name="fileToUpload" id="fileToUpload" multiple="multiple" onchange="fileSelected();" /></td></tr>
<tr><td align="center"><input type="button" onclick="uploadFile()" value="上传文件" /></td></tr>
<tr><td align="center">
<div id="fileName"></div>
<div id="fileSize"></div>
<div id="fileType"></div>
<progress id="progress" value="0" max="100"></progress>
<div id="divprogress"></div>
</td></tr>
</table>
</form>
<script>
function fileSelected() {
var file = document.getElementById('fileToUpload').files[0];
if (file) {
var fileSize = 0;
if (fileSize > 1024 * 1024) {
fileSize = (Math.round(file.size * 100 / (1024 * 1024)) / 100).toString() + 'MB';
} else {
fileSize = (Math.round(file.size * 100 / 1024) / 100).toString() + 'KB';
}
document.getElementById('fileName').innerHTML = '文件名:' + file.name;
document.getElementById('fileSize').innerHTML = '文件大小:' + fileSize;
document.getElementById('fileType').innerHTML = '文件类型:' + file.type;
}
}
function uploadFile() {
var fd = new FormData();
fd.append("fileToUpload", document.getElementById('fileToUplaod').files[0]);
var xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else if (window.ActiveXObject) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
xhr.upload.addEventListener("progress", uploadProgress, false);
xhr.addEventListener("load", uploadComplete, false);
xhr.addEventListener("error", uploadFailed, false);
xhr.addEventListener("abort", uploadCanceled, false);
xhr.open("POST", "upfile.php");
xhr.send(fd);
}
function uploadProgress(evt) {
if (evt.lengthComputable) {
var percentComplete = Math.round(evt.loaded * 100 / evt.total);
document.getElementById('divprogress').innerHTML = percentComplete.toString() + '%';
document.getElementById('progress').value = percentComplete;
} else {
document.getElementById('divprogress').innerHTML = 'unable to compute';
}
}
function uploadComplete() {
document.write(evt.target.responseText);
}
function uploadFailed() {
alert("上传过程中出现错误。");
}
function uploadCanceled(evt) {
alert("上传过程被取消。");
}
</script>
</body>
</html>
2、服务器端处理上传文件的脚本设计
<?php
// 检查上传文件的目录
$upload_dir = getcwd()."\\upload\\";
$newfile = $upload_dir.$_FILES['fileToUpload']['name'];
// 如果目录不存在,则创建
if (!is_dir($upload_dir)) {
mkdir($upload_dir);
}
if (file_exists($_FILES['fileToUpload']['tmp_name'])) {
move_uploaded_file($_FILES['fileToUpload']['tmp_name'], $newfile);
}
else {
echo ("Failed...");
}
echo ("newfile:".$newfile."<br>");
echo ("filename1:".$_FILES['fileToUpload']['name']."<br>");
echo ("filetype:".$_FILES['fileToUpload']['type']."<br>");
echo ("filesize:".$_FILES['fileToUpload']['size']."<br>");
echo ("tempfile:".$_FILES['fileToUpload']['tmp_name']."<br>");
?>
3、WebSocket
WebSocket接口是HTML5的一部分,它定义了一个全双工的Socket连接。通过此连接,可以在客户端和服务器之间传送消息。使用WebSocket技术可以大大简化双向Web通信和连接管理的复杂度。
1)什么是Socket
在TCP/IP网络环境中,可以使用Socket接口来建立网络连接,实现主机之间的数据传输。
(1)Socket的工作原理
Socket的中文意思是套接字,它是TCP/IP网络环境下应用程序与底层通信驱动程序之间的开发接口,它可以将应用程序与具体的TCP/IP隔离开来,使得应用程序不需要了解TCP/IP的具体细节,就能够实现数据传输。
在网络应用程序中, 实现基于TCP的网络通信与现实生活中的打电话有很多相似之处。如果两个人希望通过电话进行沟通,则必须满足下面的条件。
(1)拨打电话的一方需要知道对方的电话号码。如果对方使用的是内线电话,则还需要知道分机号码。而被拨打的电话不需要知道对方的号码。
(2)被拨打的电话号码必须已经启用,而且将电话线连接到电话机上。
(3)被拨打电话的主人有空闲时间可以接听电话,如果长期无人接听,则会自动挂断电话。
(4)双方必须使用相同的语言进行通话。这一条看似有些多余,但如果真的一个说汉语,另一个却说英语,那也是没有办法正常沟通的。
(5)在通话过程中,物理线路必须保持通畅,否则电话将会被挂断。
(6)在通话过程中,任何一方都可以主动挂断电话。
在网络应用程序中,Socket通信是基于客户端/服务器结构的。客户端是发送数据的一方,服务器则时刻准备着接收来自客户端的数据,并对客户端做出响应。下面是基于TCP的两个网络应用程序进行通信的基本过程。
(1)客户端(相当于拨打电话的一方)需要了解服务器的地址(相当于电话号码)。在TCP/IP网络环境中,可以使用IP地址来标识一个主机。但仅仅使用IP地址是不够的,如果一台主机中运行了多个网络应用程序,那么如何确定与哪个应用程序通信呢。在Socket通信过程中借用了TCP和UDP中端口的概念,不同的应用程序可以使用不同的端口进行通信,这样一个主机上就可以同时有多个应用程序进行网络通信了。这有些类似于电话分机的作用。
(2)服务器应用程序必须早于客户端应用程序启动,并在指定的IP地址和端口上执行监听操作。如果该端口被其他应用程序占用,则服务器应用程序无法正常启动。服务器处于监听状态就类似于电话接通电话线、等待拨打的状态。
(3) 客户端在申请发送数据时,服务器端应用程序必须有足够的时间响应才能进行正常通信。否则,就好像电话已经响了,但却无人接听一样。在通常情况下,服务器应用程序都需要具备同时处理多个客户端请求的能力,如果服务器应用程序设计得不合理或者客户端的访问量过大,都有可能导致无法及时响应客户端的情况。
(4)使用Socket进行通信的双方还必须使用相同的通信协议,Socket支持的底层通信协议包括TCP和UDP两种。在通信过程中,双方还必须采用相同的字符编码格式,而且按照双方约定的方式进行通信。这就好像在通电话时,双方都采用对方能理解的语言进行沟通一样。
TCP是基于连接的通信协议,两台计算机之间需要建立稳定可靠的连接,并在该连接上实现可靠的数据传输。如果Socket通信是基于UDP的,则数据传输之前并不需要建立连接,这就好像发电报或者发短信一样,即使对方不在线,也可以发送数据,但并不能保证对方一定会收到数据。UDP提供了超时和重试机制,如果发送数据后指定的时间内没有得到对方的响应,则视为操作超时,而且应用程序可以指定在超时后重新发送数据的次数。
应用层 | |
Socket开发接口 | |
传输层 | |
TCP | UDP |
网络层IP | |
驱动 | |
物理层 |
Socket开发接口位于应用层和传输层之间,可以选择TCP和UDP两种传输层协议实现网络通信。
(5)在通信过程中,物理网络必须保持畅通,否则通信将会中断。
(6)通信结束后,服务器端和客户端应用程序都可以中断它们之间的连接。
(2)Socket的服务方式和类型
根据基于的底层协议不同,Socket开发接口可以提供面向连接和无连接两种服务方式。
在面向连接的服务方式中,每次完整的数据传输都要经过建立连接、使用连接和关闭连接的过程。连接相当于一个传输管道,因此在数据传输过程中,分组数据包中不需要指定目的地址。由TCP提供面向连接的虚电路。基于面向连接服务方式的经典应用包括Telnet和FTP等。
在无连接服务方式中,每次数据传输时并不需要建立连接,因此每个分组数据包中必须包含完整的目的地址,并且每个数据包都独立地在网络中传输。无连接服务不能保证分组的先后顺序,不能保证数据传输的可靠性。由UDP提供无连接的数据报服务。基于无连接服务的经典应用包括简单网络管理协议(SNMP)等。
在Socket通信中,套接字分为3种类型,即流式套接字(SOCK_STREAM)、数据报式套接字(SOCK_DGRAM)和原始套接字(SOCK_RAW)。
(1)流式套接字
流式套接字提供面向连接的、可靠的数据传输服务,可以无差错地发送数据。传输数据可以是双向的字节流,即应用程序采用全双工方式,通过套接字同时传输和接收数据。
应用程序可以通过流传递有序的、不重复的数据。所谓“有序”,是指数据包按发送顺序送达目的地址,所谓“不重复”,是指一个特定的数据包只能获取一次。
如果必须保证数据能够可靠地传送到目的地,并且数据量很大时,可以采用流式套接字传输数据。WebSocket就是流式套接字。
(2)数据报式套接字
数据报式套接字提供无连接的数据传输服务。数据包被独立发送,数据可能丢失或重复。流式套接字和数据报式套接字的区别如下所示。
比较项目 | 流式套接字 | 数据报式套接字 |
---|---|---|
建立和释放连接 | √ | × |
保证数据到达 | √ | × |
按发送顺序接收数据 | √ | × |
通信数据包含完整的目的地址信息 | × | √ |
(3)原始套接字
原始套接字是公开的套接字编程接口,使用它可以在IP层上对套接字进行编程,发送和接收IP层上的原始数据包,如ICMP、TCP和UDP等协议的数据包。
2)WebSocket API 概述
过去,Socket都是应用在C/S应用程序中的,Web应用程序无法实现Socket编程。HTML5定义了WebSocket API,使用WebSocket API,可以使Web应用程序与服务器端进程保持双向通信。
提示:WebSocket可以穿透防火墙和代理服务器与多个应用程序进行通信。
WebSocket协议可以在现有的Web基础结构下很好地工作,它定义WebSocket连接的生命周期开始于HTTP连接,从而保证了与之前的Web应用程序的兼容性。经过WebSocket握手后,协议才从HTTP切换到WebSocket。
默认情况下,WebSocket连接与HTTP连接使用相同的端口(HTTP连接的默认端口为80,HTTPS连接的默认端口为443)。
一旦建立连接,WebSocket数据帧就可以在客户端和服务器之间以全双工模式发送和传回。文本和二进制数据帧可以同时传输。数据帧最少包含两个字节的数据。如果是文本数据帧,则以0x00开头,以0xFF结束,其间包含UTF-8数据。
3)WebSocket API 编程
(1)检测浏览器是否支持WebSocket API
可以使用window.WebSocket属性检测浏览器对WebSocket API 的支持情况。如果window.WebSocket等于True,则表明当前浏览器支持WebSocket API;否则表明不支持。
在网页中定义一个按钮,单击此按钮时,检测浏览器是否支持WebSocket API。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<button id="check" onclick="check();">检测</button>
<script>
function check() {
if (window.WebSocket) {
alert("您的浏览器支持WebSocket API。");
} else {
alert("您的浏览器不支持WebSocket API。");
}
}
</script>
</body>
</html>
各主流浏览器对跨文档消息机制的支持情况如下表所示。
浏览器 | 对跨文档消息机制的支持情况 |
---|---|
Chrome | 14.0及以后的版本支持 |
Firefox | 6.0及以后的版本支持 |
Internet Explorer | 10.0及以后的版本支持 |
Opera | 10.7及以后的版本支持 |
Safari | 5.0及以后的版本支持 |
(2)创建一个WebSocket实例
可以使用new关键字创建一个WebSocket实例,语法如下。
var socket = new WebSocket('ws://服务器域名或地址:端口');
例如,如果服务器架设在本地,端口为8080,则可以使用下面的代码创建一个WebSocket实例。
var socket = new WebSocket('ws://localhost:8080');
(3)发送消息
建立连接后,可以调用WebSocket对象的send()方法发送消息,语法如下。
socket.send(消息内容);
(4)关闭WebSocket连接
调用WebSocket对象的close()方法关闭WebSocket连接,语法如下。
socket.close();
(5)WebSocket对象的事件
事件 | 具体描述 | 处理函数 |
---|---|---|
open | 建立WebSocket连接时触发 | onopen |
message | 当收到消息时触发 | onmessage |
close | 当WebSocket连接关闭时触发 | onclose |
演示WebSocket通信方法的例子。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
<style>
* {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<output id="msg"></output>
<button id="send" onclick="sendmsg();">发送信息</button>
<script>
var socket = new WebSocket('ws://localhost:8080');
function sendmsg() {
if (window.WebSocket) {
socket.send("Hello, WebSocket!");
} else {
alert("您的浏览器不支持WebSocket API。");
}
}
socket.onopen = function () {
document.getElementById("msg").value = "已建立连接";
}
socket.onmessage = function (e) {
document.getElementById("msg").value = "收到消息:" + e.data;
}
socket.onclose = function (e) {
document.getElementById("msg").value = "连接已关闭";
}
</script>
</body>
</html>
提示:需要配置好WebSocket服务器后,才能成功运行上述代码。