IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Linux | Open source  >

功能丰富的 Perl: Perl 和 Amazon 云,第 5 部分

了解完整 mod_perl 站点的模板

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码

英文原文

英文原文


级别: 中级

Teodor Zlatanov, 程序员, Gold Software Systems

2009 年 7 月 13 日

这个 共分 5 部分的系列文章 向您介绍了如何使用 Perl 和 Apache 构建一个照片共享网站,从而访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB。在这最后一期文章中,我们将考察完整 mod_perl 站点的模板,包括一个用于索引的模板、三个用于上传的模板(通用模板、S3 表单和 URL 添加物)、一个用于浏览照片和评论的模板,以及一个用于递归式地浏览(即遍历浏览)照片评论的模板。
云计算空间

您是否希望随时获取最新的云计算消息?是否想得到云计算相关的技术知识?developerWorks 云计算空间就是这样一个云计算信息资源的门户,在这里您可以了解来自 IBM 和业界其他媒体的最新信息,并且得到如何在云环境中使用 IBM 软件的入门知识。

IBM 在 Amazon EC2 云计算环境中提供了 DB2、Informix、Lotus、WebSphere 等方面的 AMI 镜像资源。您只需按使用量支付少量费用,就可以使用到云上的数据、门户、Web 内容管理、情景应用等服务。欢迎您随时访问 云计算空间,获取更多信息。

在最后一期文章中,我们将了解完整的 mod_perl 站点(这一次将讨论模板;代码库位于 功能丰富的 Perl:Perl 和 Amazon 云,第 4 部分)。我再一次鼓励您阅读源代码。该站点是功能性的,但是许多细节都没有在本系列中详细介绍,我希望您能理解这些细节或者了解还存在疑问的地方。您可以通过书店或搜索引擎查找信息。

从本系列中获得最大收益

本系列需要您掌握 HTTP 和 HTML 的入门级知识,以及 JavaScript 和 Perl(位于 Apache mod_perl 进程内部)的中级知识。掌握关系数据库、磁盘存储和网络知识也非常有帮助。本系列将逐渐引入更多技术内容,因此如果需要获得有关任何主题的帮助,请参见 参考资料 小节)。

特别是,设置一个完整的 mod_perl 站点并使用 Template Toolkit 是非常广泛的主题,并且已经介绍过许多次,因此这里不再解释。最佳学习途径就是了解每一个问题和难点,直到网站可以正常运行。本系列将给出可以使网站正常运行所需的所有内容 — 但是需要由您来将所有内容结合起来。

和此前的文章一样,我将使用 share.lifelogs.com 作为域名。当在您自己的环境中使用时,应根据需要修改它。

index.tmpl

我们将按照从上而下的顺序讨论模板(policy.tmpl第 4 部分 已作讨论)。有关 Template Toolkit 语法的解释,见 参考资料 小节。我将解释比较复杂的部分。

index.tmpl 是一个简单的 HTML 页面。这里惟一需要注意的是所有 URI 都是相关的,因此这个模板和其他所有模板都可以用于任何域。


清单 1. index.tmpl,简单的 HTML
				
<html>
  <head>
<title>Share Pictures</title>
</head>
<body>
<h1>Share Pictures</h1>

You can
<a href="/upload">upload or add images</a>
or
<a href="/browse">browse images and comments</a>.

<address>
Contact <a href="mailto:tzz@bu.edu">Ted Zlatanov</a> if you have lots
of money you're trying to get out of Nigeria.  The breath
is <strike>baited</strike>bated.
</address>
</body>
</html>





回页首


upload.tmpl

现在,我们将了解一个优秀的模板,它集合了 JavaScript、HTML 和 Template Toolkit 语言。如果这还不能让 Web 设计师激动不已的话,那么我不知道还有什么能够拥有这种魔力。


清单 2. 足以让 Web 设计人员激动不已的 upload.tmpl
				
<html> 
  <head>
    <title>Upload Page For [% username %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
    <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js"
                 type="text/javascript"></script>
  </head>

  <body> 
  <script language="JavaScript">
function OnSubmitForm()
{
 var form = $('uploader');
 var file = form['file'];
 var ct   = form['Content-Type'];
 var name = form['name'].value;

 if (!name || name.length < 1)
 {
  alert("Sorry, you can't upload without a name.");
  return false;
 }

 var filename = ''+$F(file);
 var f = filename.toLowerCase(); // always compare against the lowercase version

 if (!navigator['mimeTypes'])
 {
  alert("Sorry, your browser can't tell us what type of file you're uploading.");
  return false;
 }

 var type = $A(navigator.mimeTypes).detect(function(m)
 {
  // does any of the suffixes match?
  return m.type.length > 3 && m.type.match('/') &&
   $A(m.suffixes.split(',')).detect(function(suffix)
  {
    return f.match('\.' + suffix.toLowerCase() + '$');
  });
 });

 if (!type || !type['type'])
 {
  type = { type : prompt("Enter your own MIME type, we couldn't find one through
                          the browser", "image/jpeg") };
 }

 if (type && type['type'])
 {
  ct.value = type.type;

  // fix up the redirect if we're about to submit
  var sar  = form['success_action_redirect'];

  sar.value = sar.value + escape(name);
  return true;
 }

 alert("Sorry, we don't know the type for file " + filename);
 return false;
}
</script>
<h1>Hi, [% username %]</h1>
    <form id="uploader" action="https://images.share.lifelogs.com.s3.amazonaws.com/"
          method="post" enctype="multipart/form-data" onSubmit="return OnSubmitForm();">
      <input type="hidden" name="key" value="${filename}">
      <input type="hidden" name="AWSAccessKeyId" value="[% env.AWS_KEY %]"> 
      <input type="hidden" name="acl" value="public-read">
      <input type="hidden" name="success_action_redirect"
             value="http://share.lifelogs.com/s3uploaded?user=[% username %]&name=">
      <input type="hidden" name="policy" value="[% policy %]">
      <input type="hidden" name="Content-Type" value="image/jpeg">
      <input type="hidden" name="signature" value="[% signature %]">
      Select File to upload to S3:
      <input name="file" type="file"> 
      <br>
      Enter a Name:
      <input name="name" type="text"> 
      <br> 
      <input type="submit" value="Upload File to S3"> 
    </form> 
    <form id="adder" action="/urluploaded" method="post" enctype="multipart/form-data">
      <input type="hidden" name="user" value="[% username %]">
      Enter a URL:
      <input name="url" type="text"> 
      <br>
      Enter a Name:
      <input name="name" type="text"> 
      <br>
      <input type="submit" value="Add URL"> 
    </form> 
  </body>
</html>

上传页面展示了两个上传对话框。它们都可以添加照片,但是第二个对话框更加简单。在第二个对话框中,用户填入照片的 URL 和名称,这些内容随后被 POST/urluploaded,后者正是 urluploaded.tmpl。在显示该模板时,将自动调用图像参数处理程序。用户名从服务器获得并且是一个隐藏了表单的 POST 参数。

第一个表单非常复杂。幸运的是,可以阅读本系列的 第 2 部分,其中解释了有关 S3 上传的所有内容,因此您应当不会感到陌生。

对第 2 部分的 s3form.pl(包含在 下载 小节)的主要修改包括:

  • success_action_redirect 将用户名和照片名作为参数传递。策略被修改为只需要字符串中截至用户名的一部分,但是不包括用户名。
  • 策略和签名从服务器传递。
  • AWS 访问和秘密密匙被放到 env 散列中从服务器传递。
  • OnSubmitForm 函数需要一个名称并将其作为参数添加到 success_action_redirect 表单字段并转义(注意,在决定 MIME 类型之前 需要用到名称,但是名称只有在对表单执行 POST 前被添加到 URL。)
  • 如果没有找到 MIME 类型,OnSubmitForm 函数就会失败,允许用户指定他们自己的类型。




回页首


s3uploaded.tmpl

在 S3 上传中将使用该模板。


清单 3. 在上传中使用 s3uploaded.tmpl 模板
				
[% success = params.result %]
<html> 
  <head>
    <title>[% IF success %]Successful[% ELSE %]Unsuccessful[% END %]
              Upload Page For [% params.user %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    [% IF success %]Congratulations[% ELSE %]Sorry[% END %], [% params.user %].
       You have [% IF success %]successfully[% ELSE %]unsuccessfully[% END %]
       uploaded [% params.key %] to S3 bucket [% params.bucket %]
       named [% params.name %].<p>
      (etag is [% params.etag %] but I doubt you care.)
    <p>
[% IF success %]
      <a href="http://[% params.bucket %].s3.amazonaws.com/[% params.key %]">
   Your new upload is probably here.  Let's see if it displays already.
   <img src="http://[% params.bucket %].s3.amazonaws.com/[% params.key %]">
      </a>
[% END %]
    <p>
      You can now go back to <a href="/upload">uploading</a> or
      <a href="/">the main page</a>.
  </body>
</html>

通过一些复杂的 Template Toolkit IF-ELSE 结构和 result 参数,页面可以处理成功的和不成功的上传。成功上传的关键在于添加了 SimpleDB;如果掌握了这点,那么就能够始终成功地将照片上传到 S3。在 SimpleDB 故障时从 S3 取回照片留给读者作为练习。





回页首


urluploaded.tmpl

该模板用于 URL 添加。


清单 4. urluploaded.tmpl 实现代码重用
				
[% success = params.result %]
<html> 
  <head>
    <title>[% IF success %]Successful[% ELSE %]Unsuccessful[% END %]
              URL add Page For [% params.user %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    [% IF success %]Congratulations[% ELSE %]Sorry[% END %], [% params.user %].
       You have [% IF success %]successfully[% ELSE %]unsuccessfully[% END %]
       added [% params.url %] named [% params.name %].<p>
    <p>
[% IF success %]
      <a href="[% params.url %]">
   The URL you added is, perhaps, visible here.
   <img src="[% params.url %]">
      </a>
[% END %]
    <p>
      You can now go back to <a href="/upload">uploading</a>
      or <a href="/">the main page</a>.
  </body>
</html>

除了明显糟糕的 HTML 外,该模板类似于 s3uploaded.tmpl。通过使用这个模板,代码重用不再是问题。





回页首


browse.tmpl

该模板可以浏览照片和评论。


清单 5. 使用 browse.tmpl 浏览照片和评论
				
[% SET images = fimages() %]
[% SET comments = fcomments() %]
<html> 
  <head>
    <title>Browse</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    <ul>
      [% FOR ik IN images.keys %]
      <li>
         [% SET image = images.$ik %]
         [% image.name %]<br>
         <img src="[% image.url %]"><br>
         [% IF image.bucket %](in S3)[% END %]<br>
         uploaded by [% image.user %]<br>
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="deleteimageid" value="[% ik %]">
           <input type="submit" value="Delete"> 
         </form> 
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="imageid" value="[% ik %]">
           Change Image Name:
           <input name="name" type="text" value="[% image.name|html %]"> 
           <input type="submit" value="Rename"> 
         </form>
         [% INCLUDE comments.tmpl ik=ik comments=comments %]
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="user" value="[% username %]">
           <input type="hidden" name="refimageid" value="[% ik %]">
           Enter a Comment (as user [% username %]):
           <input name="comment" type="text"> 
           <input type="submit" value="Comment"> 
         </form> 
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="refimageid" value="[% ik %]">
           Enter Anonymous Comment:
           <input name="comment" type="text"> 
           <input type="submit" value="Comment"> 
         </form> 
      </li>
      [% END %]
    </ul>
  </body>
</html>

您可以使用这个模板浏览所有的照片和评论。但是这样做不太有效,而且在拥有数千照片和评论的真实网站中,这个模板几乎肯定会失效。您需要使用 SimpleDB 的优秀的 NextToken 分页模式(在这里 SimpleDB 实用函数没有有效地使用该分页模式)或您自己的工具对照片设置分页,然后只获得将要显示的照片的评论。

您肯定希望只在需要的时候收到评论。SimpleDB 请求的开销很大。因此模板每次都需要将评论传递到 comments.tmpl 模板。

模板使用了 Template Toolkit FOR 循环来遍历(未分类的)照片(如果需要进行分类,最好在 Template Toolkit 环境之外使用 Perl 代码完成这个操作)。这里要求使用照片键来选择与之匹配的评论。对于每张照片,您将显示照片名、URL、S3 状态、所有者。然后显示各种表单来删除照片、修改照片名或以匿名方式或作为用户发布评论。所有操作都很简单。

最后,comments.tmpl 模板和(INCLUDE)一些参数被包含在一起 — ik 是照片键而 comments 是评论列表 — 表示无论模板生成什么内容,都将针对每张照片从照片列表中删除。





回页首


comments.tmpl

该模板针对某张照片递归地浏览评论(遍历评论)。


清单 6. 使用 comments.tmpl 遍历评论
				
[% IF parent %]
 [% SET thread = comments.$ik.$parent %]
[% ELSE %]
 [% SET thread = comments.$ik.noparent %]
[% END %]

<ul>
[% FOR ck IN thread.keys %]
 [% SET comment = thread.$ck %]
  <li>[% comment.comment %] (by [% IF comment.user %][% comment.user %]
      [% ELSE %]Anonymous[% END %])<br>
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="deletecommentid" value="[% ck %]">
      <input type="submit" value="Delete"> 
    </form> 
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="commentid" value="[% ck %]">
      Edit Comment:
      <input name="comment" type="text" value="[% comment.comment|html %]"> 
      <input type="submit" value="Edit"> 
    </form>
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="user" value="[% username %]">
      <input type="hidden" name="refimageid" value="[% ik %]">
      <input type="hidden" name="refcommentid" value="[% ck %]">
      Enter a Comment (as user [% username %]):
      <input name="comment" type="text"> 
      <input type="submit" value="Comment"> 
    </form> 
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="refimageid" value="[% ik %]">
      <input type="hidden" name="refcommentid" value="[% ck %]">
      Enter Anonymous Comment:
      <input name="comment" type="text"> 
      <input type="submit" value="Comment"> 
    </form> 
    [% INCLUDE comments.tmpl ik=ik comments=comments parent=ck %]
  </li>
[% END %]
</ul>

我将最好的、最困难的代码留到了最后。

对于照片键,该模板将查找该照片的所有评论,照片的父元素为一个特定的评论键。

对于相关的每张照片(具有给定的父元素或没有父元素,视情况而定),模板将显示评论本身,紧接着显示 HTML 表单来删除评论、进行编辑或输入新评论(以用户名或匿名的方式)。这与 browse.tmpl 中的表单几乎完全相同,惟一不同的是这里包含了 refcommentid 参数。

接下来是比较棘手的部分,这个模板针对找到的每个评论包含它自身,每次将 parent 设置为评论键的值。因此在伪语言(Template Toolkit)中使用递归,从而在 Perl 下运行的布局语言(HTML)中生成递归。





回页首


结束语

这份共 5 部分的系列文章现在已经全部结束。您在本文中了解了完整 mod_perl 站点的模板 — 使用了来自第 2 部分和第 3 部分的内容以及第 4 部分生成的代码。该站点使用 Template Toolkit、S3 和 SimpleDB 提供了照片上传、浏览、编辑、删除以及添加评论功能。






回页首


下载

描述名字大小下载方法
SimpleDB 实用函数simpledb_utility.zip3KBHTTP
样例脚本(来自第 2 部分)s3form.zip2KBHTTP
样例脚本(来自第 3 部分)simple_go.zip4KBHTTP
关于下载方法的信息


参考资料

学习
  • 从 “功能丰富的 Perl:Perl 和 Amazon 云,第 1 部分”(developerWorks,2009 年 3 月)开始,理解在网站构建中使用 Amazon S3 和 SimpleDB 的优点和缺点。然后继续阅读 第 2 部分(2009 年 4 月),从网页中通过一个 HTML 表单将文件上传到 S3,最小化服务器上的负载。接着阅读 第 3 部分(2009 年 6 月),在一个表中通过 URL 列表上传照片并管理照片和评论。最后阅读 第 4 部分 遍历整个站点的代码库。

  • Amazon.com 提供了服务产品细节和开发人员资源:
  • 了解令人惊讶的 JavaScript MimeType,它是导航器对象的一个属性,导航器对象是一个顶级对象,是所使用的客户机 Internet 浏览器或 Web 导航器程序的对象表示。

  • mod_perl 将 Perl 和 Apache HTTP 服务器的全部威力结合起来。

  • Prototype 是一个 JavaScript 框架,可以通过一个类驱动开发工具箱和目前为止最好的 Ajax 库简化动态 Web 应用程序的开发。并且提供了介绍如何使用它的优秀文章:Sergio Pereira 撰写的 “Developer Notes for prototype.js”。

  • S3 和 SimpleDB 是基于 Web 的服务,因此会出现 中断,所以您需要认真考虑。

  • Amazon 服务的云计算是一个热点话题。本系列就介绍了使用 Perl 访问这些服务,但是要获得有关此产品的更广泛的概述,请阅读系列文章 “用 Amazon Web Services 进行云计算”(developerWorks,2008 年 7 月至 2009 年 2 月)。

  • developerWorks Linux 专区 寻找为 Linux 开发人员(包括 Linux 新手入门)准备的更多参考资料,查阅我们 最受欢迎的文章和教程

  • 在 developerWorks 上查阅所有 Linux 技巧 Linux 教程

  • 随时关注 developerWorks 技术活动网络广播


获得产品和技术

讨论
  • 加入 My developerWorks 社区;您可以通过个人档案和定制主页获得符合自己的兴趣的 developerWorks 文章,并与其他 developerWorks 用户进行交流。


关于作者

Teodor Zlatanov 1999 年毕业于波士顿大学计算机工程系,他获得了硕士学位。他从 1992 年就开始做程序员,使用过 Perl、Java、C 和 C++。他的兴趣是使用开源技术进行文本解析、数据库架构、用户界面和 Unix 系统管理。




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款