图 1. 分页器提供了显示用户照片的一种方式 |
/* * Find a list of images in /images and provide thumbnails */ function get_table ( $limit_start = 0, $limit_step = 5 ) { $images = get_image_list('images'); // Generate navigation for Previous and Next buttons // Code given below $output .= '<table class="image_table">'; $columns = 5; foreach ($images as $index => $image) { // Begin directory listing at item number $limit_start if ( $index < $limit_start ) continue; // End directory listing at item number $limit_end if ( $index >= $limit_start + $limit_step ) continue; // Begin column if ( $index - $limit_start % $columns == 0 ) { $output .= '<tr>'; } // Generate link to blown up image (see below) $thumbnail = '<img src="thumbnails/' . $image . '" />'; $output .= '<td>' . get_image_link($thumbnail, $index) . '</td>'; // Close column if ( $index - $limit_start % $columns == $columns - 1 ) { $output .= '</tr>'; } } $output .= '</table>'; return $nav . $output; } |
function get_image_list ( $image_dir ) { $d = dir($image_dir); $files = array(); if ( !$d ) return null; while (false !== ($file = $d->read())) { // getimagesize returns true only on valid images if ( @getimagesize( $image_dir . '/' . $file ) ) { $files[] = $file; } } $d->close(); return $files; } |
注意:本文后面还要使用 get_file_list() 函数。有一点很重要,无论何时调用该函数,返回的数组都是不变的。因为提供的实现要进行目录搜索,必须保证目录中的指定文件不会改变,每次都要按字母顺序排序。
导航的实现
虽然表格列出了目录中的一些图像,但用户还需要一种查看表格中未出现的图片的方法。要真正实现分页器的导行,则需要一套标准的链接:首页、上一页、下一页和尾页。
清单 3. 分页器导航
// Append navigation $output = '<h4>Showing items ' . $limit_start . '-' . min($limit_start + $limit_step - 1, count($images)) . ' of ' . count($images) . '<br />'; $prev_start = max(0, $limit_start - $limit_step); if ( $limit_start > 0 ) { $output .= get_table_link('<<', 0, $limit_step); $output .= ' | ' . get_table_link('Prev', $prev_start, $limit_step); } else { $output .= '<< | Prev'; } // Append next button $next_start = min($limit_start + $limit_step, count($images)); if ( $limit_start + $limit_step < count($images) ) { $output .= ' | ' . get_table_link('Next',$next_start, $limit_step); $output .= ' | ' . get_table_link('>>',(count($images) - $limit_step), $limit_step); } else { $output .= ' | Next | >>'; } $output .= '</h4>'; |
function get_table_link ( $title, $start, $step ) { $link = "index.php?start=$start&step=$step"; return '<a href="' . $link . '">' . $title .'</a>'; } function get_image_link ( $title, $index ) { $link = "expand.php?index=$index"; return '<a href="' . $link . '">' . $title . '</a>'; } |
function get_image ( $index ) { $images = get_image_list ( 'images' ); // Generate navigation $output .= '<img src="images/' . $images[$index] . '" />'; return $output; } |
$output .= '<h4>Viewing image ' . $index .' of ' . count($images) . '<br />'; if ( $index > 0 ) { $output .= get_image_link('<<', 0); $output .= ' | ' . get_image_link('Prev', $index-1); } else { $output .= '<< | Prev'; } $output .= ' | ' . get_table_link('Up', $index, 5); if ( $index < count($images) ) { $output .= ' | ' . get_image_link('Next', $index+1); $output .= ' | ' . get_image_link('>>', count($images)); } else { $output .= ' | Next | >>'; } $output .= '</h4>'; |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/2000/REC-xhtml1-20000126/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>Creating a simple picture album viewer</title> <style type="text/css"> body { text-align: center } table.image_table { margin: 0 auto 0 auto; width: 700px; padding:10px; border: 1px solid #ccc; background: #eee; } table.image_table td { padding: 5px } table.image_table a { display: block; } table.image_table img { display: block; width: 120px; padding: 2px; border: 1px solid #ccc; } </style> </head> <body> <h1>Creating a simple picture album viewer</h1> <?php $index = isset($_REQUEST['index']) ? $_REQUEST['index'] : 0; echo get_image($index); ?> </body> </html> |
图 2. 完成的相册 |
添加 Sajax
现在相册提供了基本的导航功能,目录中的图像添加了索引。下面您将看到添加 Sajax 能够改进编程和用户体验。
这里假设您对 Ajax 有基本的了解,最好还熟悉 Sajax 的基础知识(请参阅 参考资料 中的教程)。
Sajax、Ajax 与传统 Web 应用程序
现在我们已经使用标准的 Web 开发模型开发了应用程序。两项主要功能是分页器和图像查看器,它们分别对应不同的 PHP 文件。参数作为 HTTP GET 请求传递给脚本,脚本直接向 Web 客户机返回页面。
图 3. 在传统的 Web 应用程序中,三个不同的请求调用了两个页面 |
Web 开发社区的人都知道,Ajax 允许向服务器发出异步的辅助请求,并直接在网页中显示结果(如图 4 所示)。不幸的是,即便最简单的 Ajax 应用程序实现起来也是一项大任务。因为 Ajax 不是标准化的技术,Internet Explorer 和其他浏览器(如 Firefox、Safari)的实现是不同的。此外,程序员至少要编写三个函数才能实现一个功能,这三个函数是:发送 HTTP 请求的初始 JavaScript,返回响应的 PHP 脚本,以及另一个处理这些响应的 JavaScript 函数。
建立在 Ajax 库之上的 Sajax 通过运用简单的启发式方法大大简化了这一过程:Web 客户机需要访问的每个 PHP 函数都由 Sajax “导出”。如果有一个名为 foo_bar() 的 PHP 函数,那么 Sajax 会把该函数导出为 JavaScript 函数 x_foo_bar()。客户机对 x_foo_bar() 的任何调用都会自动转发给服务器上的 foo_bar(),输出则传递给另一个 JavaScript 函数。清单 8 中的简短页面示范了这种功能。运行这个例子需要下载 Sajax 库(请参阅 参考资料)。
清单 8. Sajax 的应用
<?php require("Sajax.php"); function foo_bar ( $param ) { return "You typed: $param"; } $sajax_request_type = "GET"; // Set HTTP request type to GET sajax_init(); // Prepare Sajax sajax_export("foo_bar"); // foo_bar can now be called by client sajax_handle_client_request(); // Discussed below ?> <html> <head> <script language="javascript"> <? sajax_show_javascript(); ?> </script> </head> <body> <form onSubmit="x_foo_bar(this.input.value, alert);return false;"> <input type="text" name="input" /> </form> </body> </html> |
如果打开清单 8 中的页面,在输入框中输入一些内容然后单击 Enter,那么输入内容就会在一个警告框中显示出来。但在这个看似简单的网页背后,x_foo_bar() JavaScript 函数将远程调用 foo_bar() 函数,并把响应传递给 JavaScript 内置函数 alert()。每个 Sajax 导出函数的最后一个参数都是一个响应处理程序,负责处理 foo_bar() 的输出。
这个例子还说明了 Sajax 快速开发的另一个重要特性:不需要每个函数都有一个单独的文件,页面实际上调用的是其自身,因此更便于跟踪函数的调用(如图 5 所示)。x_foo_bar() 函数直接向页面发回 Ajax 请求,在请求中包含函数名和参数。关键是 sajax_handle_client_request() 函数,它截获所有的 Sajax 调用并自动对它们进行处理。
图 5. 使用 Sajax 客户机可通过一个页面访问服务器端的多个函数 |
<?php require("Sajax.php"); function get_image () { } // Defined later function get_thumbs_table () { } // Defined later // Standard Sajax stuff. Use Get, and export two // main functions to javascript $sajax_request_type = "GET"; sajax_init(); sajax_export("get_thumbs_table", "get_image"); sajax_handle_client_request(); ?> |
<body> <h1>Sajax photo album</h1> <div id="window"></div> </body> |
<head> <title>Creating a Sajax photo album</title> <style type="text/css"> body { text-align: center } div#window { margin: 0 auto 0 auto; width: 700px; padding: 10px; border: 1px solid #ccc; background: #eee; } table.image_table { margin: 0 auto 0 auto; } table.image_table td { padding: 5px } table.image_table a { display: block; } table.image_table img { display: block; width: 120px padding: 2px; border: 1px solid #ccc; } img.full { display: block; margin: 0 auto 0 auto; width: 300px; border: 1px solid #000 } </style> <script language="javascript"> <? sajax_show_javascript(); ?> // Outputs directly to the "window" div function to_window(output) { document.getElementById("window").innerHTML = output; } window.onload = function() { x get table to window); }; </script> </head> |
图 6. 完成的基于 Sajax 的相册(缩略图) |
可以看到 URL 仍然保持不变,并带来了更多愉快的用户体验。window div 显示在一个灰色的框中,通过 Sajax 生成的内容非常清楚。脚本不一定要知道自身或者它在服务器上的位置,因为所有的链接最终都成为直接对页面自身的 JavaScript 调用。因此我们的代码能够很好的模块化。我们只需要保持 JavaScript 和 PHP 函数在同一个页面上即可,即使页面位置发生了变化也没有关系。
扩展相册
使用 Sajax 把我们的相册变成活动的 Web 应用程序如此轻而易举,我们要再花点时间添加一些功能,进一步说明 Sajax 如何使从服务器检索数据变得完全透明。我们将为相册添加元数据功能,这样用户就能为他们的图片添加说明。
元数据
没有上下文说明的相册是不完整的,比如照片的来源、作者等。为此我们要将图像集中起来创建一个简单的 XML 文件。根节点是 gallery,它包含任意多个 photo 节点。每个 photo 节点都通过其 file 属性来编号。在 photo 节点中可以使用任意多个标记来描述照片,但本例中只使用了 date、locale 和 comment。
清单 12. 包含元数据的 XML 文件
<?xml version="1.0"?> <gallery> <photo file="image01.jpg?http://www.xvna.com"> <date>August 6, 2006</date> <locale>Los Angeles, CA</locale> <comment>Here's a photo of my favorite celebrity</comment> </photo> <photo file="image02.jpg?http://www.xvna.com"> <date>August 7, 2006</date> <locale>San Francisco, CA</locale> <comment>In SF, we got to ride the street cars</comment> </photo> <photo file="image03.jpg?http://www.xvna.com"> <date>August 8, 2006</date> <locale>Portland, OR</locale> <comment>Time to end our road trip!</comment> </photo> </gallery> |
function get_meta_data ( $file ) { // Using getimagesize, the server calculates the dimensions list($width, $height) = @getimagesize("images/$file"); $output = "<p>Width: {$width}px, Height: {$height}px</p>"; // Use SimpleXML package in PHP_v5: // http://us3.php.net/manual/en/ref.simplexml.php $xml = simplexml_load_file("gallery.xml"); foreach ( $xml as $photo ) { if ($photo['id'] == $file) { $output .= !empty($photo->date) ? "<p>Date taken:{$photo->date}</p>" : ''; $output .= !empty($photo->locale) ? "<p>Location:{$photo->locale}>/p>" : ''; $output .= !empty($photo->comment) ? "<p>Comment:{$photo->comment}</p>" : ''; } } return $output; |
function get_image ( $index ) { $images = get_image_list ( 'images' ); // ... $output .= '<img src="images/' . $images[$index] . '" />'; $output .= '<div id="meta_data">' . get_meta_data( $images[$index] ) . '</div>'; return $output; } |
图 7. 使用元数据的相册 |