要对深刻理解如何构建基于Ajax的应用程序的完整过程,我们从零开始构建一个基于Ajax的图库应用程序。这个图库是一个很常见的Web应用程序,在专业Web开发人员中是广为流行的。
类似这种图库应用所存在的问题现在大都已经解决了。因此在考虑这个图库要实现什么功能时,所讨论的主要是一些与现在因特网中的图库应用看起来有所区别的内容。
最后要考虑的是如何用Ajax概念来改进普通的图库程序。有一些场合应用Ajax是弊大于利的(在第11章有这样的例子),而我们要改进的是图库查看(有图库维护)功能。
我希望去除图像上传之外的绝大部分乏味单调的内容。大多数图库在图像的维护和上传方面用时很长(健壮性也很差)。我希望能够快速地插入图像,而不必担心图片尺寸。也希望能够在单击图像时候看到略缩图(类似MSN空间中的效果)。这样可使图库查看的过程更生动。
在本例中将不考虑所有与上传相关的事,你可以将大量图片拖到指定的图像目录中,然后该系统将遍历该目录并构建出整个结构。如果真的需要在文件中保存更多的信息,通过子文件夹来分类并以其文件名作为标题也不难。
同时我也不希望刷新屏幕。就像把图片库连接到更强大的应用程序,在每次上传新图像或查看下一个时无需重新载入应用程序的其余内容。这里,我们将使用JavaScript和Ajax来实现这些功能。
7.1 程序代码
现在先看看实现该应用程序的代码。代码清单7-1中是载入到浏览器中的主脚本,所有操作都是通过该脚本执行的。代码清单7-2展示了它使用的JavaScript代码,包括执行Ajax请求和用户界面更新。
其他代码清单(7-3到7-7)是显示表单、处理上传和输出图像等功能所需的PHP代码。在这些代码清单之后,将更进一步研究代码是如何工作的,将产生什么的结果。
代码清单7-1 图库所需的HTML框架(sample7_1.php)
<!-- sample7_1.php -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="style.css" />
<title>Sample 7_1</title>
<script type="text/javascript" src="functions.js"></script>
</head>
<body>
<h1>My Gallery</h1>
<div id="maindiv">
<!-- Big Image -->
<div id="middiv">
<?php require_once ("midpic.php"); ?>
</div>
<!-- Messages -->
<div id="errordiv"></div>
<!-- Image navigation -->
<div id="picdiv"><?php require_once ("picnav.php"); ?></div>
</div>
<h2>Add An Image</h2>
<form action="process_upload.php" method="post" target="uploadframe"
enctype="multipart/form-data" onsubmit="uploadimg(this); return false">
<input type="file" id="myfile" name="myfile" />
<input type="submit" value="Submit" />
<iframe id="uploadframe" name="uploadframe" src="process_upload.php">
</iframe>
</form>
</body>
</html>
代码清单7-2 使图库能够运行所需的JavaScript代码(functions.js)
// functions.js
function runajax(objID, serverPage)
{
//创建一个布尔变量,检查是否存在有效的IE实例。
var xmlhttp = false;
//检查使用的是否为IE浏览器。
try {
//如果JavaScript的版本大于5.0。
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
//如果不是,则使用老版本的ActiveX对象。
try {
//如果使用的是IE浏览器。
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
//否则使用的肯定是非IE浏览器。
xmlhttp = false;
}
}
//如果使用的不是IE浏览器,则创建一个该对象的JavaScript实例。
if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
xmlhttp = new XMLHttpRequest();
}
var obj = document.getElementById(objID);
xmlhttp.open("GET", serverPage);
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
obj.innerHTML = xmlhttp.responseText;
}
}
xmlhttp.send(null);
}
// 在图库刷新之前延迟的毫秒数。
var refreshrate = 1000;
//显示载入提示信息的函数。
function updateStatus()
{
document.getElementById("errordiv").innerHTML = "";
document.getElementById("middiv").innerHTML = "<b>Loading...</b>";
}
function refreshView()
{
// 重新载入全尺寸的图像。
setTimeout ('runajax ("middiv","midpic.php")',refreshrate);
// 重新载入导航工具。
setTimeout ('runajax ("picdiv","picnav.php")',refreshrate);
}
function uploadimg(theform)
{
// 更新用户状态信息。
updateStatus();
// 现在提交该表单。
theform.submit();
// 最后更新并显示。
refreshView();
}
function removeimg(theimg)
{
runajax("errordiv", "delpic.php?pic=" + theimg);
refreshView();
}
function imageClick(img)
{
updateStatus();
runajax('middiv', 'midpic.php?curimage=' + img);
runajax('picdiv', 'picnav.php?curimage=' + img);
}
代码清单7-3 管理图库的配置文件(config.php)
<?php
//config.php
// 生成图像的最大尺寸。
$GLOBALS['maxwidth'] = 500;
$GLOBALS['maxheight'] = 200;
//生成略缩图的最大尺寸。
$GLOBALS['maxwidththumb'] = 60;
$GLOBALS['maxheightthumb'] = 60;
// 保存图像和略缩图的位置。
$GLOBALS['imagesfolder'] = "images";
$GLOBALS['thumbsfolder'] = "images/thumbs";
// 允许的文件类型和MIME类型。
$GLOBALS['allowedmimetypes'] = array('image/jpeg',
'image/pjpeg',
'image/png',
'image/gif');
$GLOBALS['allowedfiletypes'] = array(
'jpg' => array('load' => 'ImageCreateFromJpeg',
'save' => 'ImageJpeg'),
'jpeg' => array('load' => 'ImageCreateFromJpeg',
'save' => 'ImageJpeg'),
'gif' => array('load' => 'ImageCreateFromGif',
'save' => 'ImageGif'),
'png' => array('load' => 'ImageCreateFromPng',
'save' => 'ImagePng')
);
// 在导航区每行图像的个数。
$GLOBALS['maxperrow'] = 7;
?>
代码清单7-4 包含图库所需PHP函数的文件(functions.php)
<?php
// functions.php
//为指定文件夹中所有图像创建一个数组的函数。
function getImages()
{
$images = array();
if (is_dir($GLOBALS['imagesfolder'])) {
$files = scandir ($GLOBALS['imagesfolder']);
foreach ($files as $file) {
$path = $GLOBALS['imagesfolder'] . '/' . $file;
if (is_file($path)) {
$pathinfo = pathinfo($path);
if (array_key_exists($pathinfo['extension'],
$GLOBALS['allowedfiletypes']))
$images[] = $file;
}
}
}
return $images;
}
// 根据允许的最大尺寸计算新的尺寸。
function calculateDimensions($width, $height, $maxWidth, $maxHeight)
{
$ret = array('w' => $width, 'h' => $height);
$ratio = $width / $height;
if ($width > $maxWidth || $height > $maxHeight) {
$ret['w'] = $maxWidth;
$ret['h'] = $ret['w'] / $ratio;
if ($ret['h'] > $maxHeight) {
$ret['h'] = $maxHeight;
$ret['w'] = $ret['h'] * $ratio;
}
}
return $ret;
}
// 修改图像尺寸的函数。
function createThumb($img, $maxWidth, $maxHeight, $ext = '')
{
$path = $GLOBALS['imagesfolder'] . '/' . basename($img);
if (!file_exists($path) || !is_file($path))
return;
$pathinfo = pathinfo($path);
$extension = $pathinfo['extension'];
if (!array_key_exists($extension, $GLOBALS['allowedfiletypes']))
return;
$cursize = getImageSize($path);
$newsize = calculateDimensions($cursize[0], $cursize[1],
$maxWidth, $maxHeight);
$newfile = preg_replace('/(\.' . preg_quote($extension, '/') . ')$/',
$ext . '\\1', $img);
$newpath = $GLOBALS['thumbsfolder'] . '/' . $newfile;
$loadfunc = $GLOBALS['allowedfiletypes'][$extension]['load'];
$savefunc = $GLOBALS['allowedfiletypes'][$extension]['save'];
$srcimage = $loadfunc($path);
$dstimage = ImageCreateTrueColor($newsize['w'], $newsize['h']);
ImageCopyResampled($dstimage, $srcimage,
0, 0, 0, 0,
$newsize['w'], $newsize['h'],
$cursize[0], $cursize[1]);
$savefunc($dstimage, $newpath);
return $newpath;
}
?>
代码清单7-5 上传文件所需的PHP代码(process_upload.php)
<?php
require_once ("config.php");
require_once ("functions.php");
// 检查上传的文件是否有效。
if (!isset($_FILES['myfile']) || $_FILES['myfile']['error'] != UPLOAD_ERR_OK)
exit;
// 检查文件类型是否有效。
if (in_array($_FILES['myfile']['type'], $GLOBALS['allowedmimetypes'])){
// 最后将该文件复制到目标目录中。
$dstPath = $GLOBALS['imagesfolder'] . '/' . $_FILES['myfile']['name'];
move_uploaded_file($_FILES['myfile']['tmp_name'], $dstPath);
}
?>
代码清单7-6 显示当前选择图像的PHP代码(midpic.php)
<?php
//midpic.php
require_once ("config.php");
require_once ("functions.php");
$imgarr = getImages();
// 如果图库中包含图像,则显示已选中图像。
// 如果没有选中的图像,则显示出第一张图像。
if (count($imgarr) > 0) {
$curimage = $_GET['curimage'];
if (!in_array($curimage, $imgarr))
$curimage = $imgarr[0];
// 当上传的内容太大时,创建一个较小的版本。
$thumb = createthumb($curimage,
$GLOBALS['maxwidth'],
$GLOBALS['maxheight'],
'_big');
if (file_exists($thumb) && is_file($thumb)) {
?>
<div id="imagecontainer">
<img src="<?= $thumb ?>" alt="" />
</div>
<div id="imageoptions">
<a href="delpic.php?pic=<?= $curimage ?>"
onclick="removeimg ('<?= $curimage ?>'); return false">
<img src="delete.png" alt="Delete image" />
</a>
</div>
<?php
}
}
else
echo "Gallery is empty.";
?>
代码清单7-7 用于展示基于略缩图的导航系统的PHP代码(picnav.php)
<?php
//picnav.php
require_once ("config.php");
require_once ("functions.php");
//计算图像总数。
$imgarr = getImages();
$numimages = count($imgarr);
//如果有一张以上的图像。
if ($numimages > 0) {
$curimage = $_GET['curimage'];
if (!in_array($curimage, $imgarr))
$curimage = $imgarr[0];
$selectedidx = array_search($curimage, $imgarr);
?>
<table id="navtable">
<tr>
<?php
$numtoshow = min($numimages, $GLOBALS['maxperrow']);
$firstidx = max(0, $selectedidx - floor($numtoshow / 2));
if ($firstidx + $numtoshow > $numimages)
$firstidx = $numimages - $numtoshow;
for ($i = $firstidx; $i < $numtoshow + $firstidx; $i++) {
$file = $imgarr[$i];
$selected = $selectedidx == $i;
$thumb = createthumb($file,
$GLOBALS['maxwidththumb'],
$GLOBALS['maxheightthumb'],
'_th');
if (!file_exists($thumb) || !is_file($thumb))
continue;
?>
<td<?php if ($selected) { ?> class="selected"<?php } ?>>
<a href="sample7_1.php?curimage=<?= $file ?>"
onclick="imageClick('<?= $file ?>'); return false">
<img src="<?= $thumb ?>" alt="" />
</a>
</td>
<?php
}
?>
</tr>
</table>
<?php
}
?>







