功能丰富的 Perl: Perl 和 Amazon 云,第 2 部分--通过 HTML 表单将数据安全地上传到 S3

这个共5个部分的系列文章将带领您使用 Perl 和 Apache 构建一个简单的照片共享网站,访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB。在这一期文章中,理解如何通过一个 HTML 表单将一个文件从 Web 页面上传到 S3,最小化服务上的负载,同时维护严格的安全策略。

您可以通过多种方式将文件从一个 Web 页面上传到 Amazon 的 Simple Storage Service (S3):

使用来自 CPAN 的合适的模块从命令行上传 使用来自 Amazon 的合适的模块从命令行上传 直接从 HTML 表单上传

本文将展示直接从一个 HTML 表单上传文件,从而最小化服务器的负载。我们将文件上传到一个定制 URL,本系列后面的文章将使用该 URL 设置所构建的照片共享站点的其他部分。我们在本系列中使用 share.lifelogs.com 作为域名。

上传到 S3

您可以通过一个 POST 表单将数据上传到 S3(也可以使用 PUT HTTP 方法和 SOAP PutObject 调用)。对于本文,我们将使用表单 POST,因为它很简单,并且没有使用服务器资源(磁盘、CPU 或带宽)。

上传到 S3 的最大问题是不能修改元数据。这可能是由 S3 的分布式特性造成的,或者是 Amazon 为了保持简单。在 S3 论坛上,Amazon 表示这一点将来可能会出现变化。

无论如何,这意味着在上传内容时必须设置 Content-Type,否则将得到非常不理想的二进制/八位字节流集合的内容类型。其他元数据则不是特别重要,因为 Amazon 的 SimpleDB 用于跟踪上传,并且您可以在其中存储元数据。我们提供了一个大部分情况下都令人满意的JavaScript 解决办法来解决 Content-Type 问题。

用户名将是一个有效的 URL 的一部分。您还应该将其放入要加载的元数据中,这样它就始终与文件相关联。但是用户随后就不能再修改他或她的用户名,除非为了存储新的元数据,要求您通过某些方法重新执行所有 S3 上传。您可以使用独立于用户名的用户 ID 并将用户 ID 与 S3 对象关联在一起,但是这添加了不必要的复杂性,因为我们的目的是构建一个简单的照片共享站点。

我们(即 Web 站点的运作者)现在在 S3 上设置了一个名为 images.share.lifelogs.com 的 bucket。这个 bucket 拥有相应的访问控制,允许执行公共的读操作。如果使用 Amazon 文档难以实现这一点,那么可以使用 S3Fox 和 JungleDisk 等工具或其他 S3 接口来设置 bucket。如果遵循这种方法,那么还将得到 AWS 访问权和密匙。

您将使用一种策略来控制上传;该策略是使用 JSON 数据格式表示的一组规则集。您将使用您的 Amazon 密匙对策略进行签名。

对策略签名是通过 Digest::HMAC_SHA1 Perl 模块完成的。您首先必须将策略转化为 Base64,去掉所有换行符,对生成的数据进行签名,使用 Base64 对签名进行编码。然后给 Ms. Elle Cowalsky 汇去 1.28 美元,并祈祷她将您所需的签名寄回来。只是开个玩笑!试试下面的代码吧:

清单 1. 对上传策略签名

my $aws_secret_access_key = 'get it from http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key';my $policy = 'entire policy here';$policy = encode_base64($policy);$policy =~ s/\n//g;my $signature = encode_base64(hmac_sha1($policy, $aws_secret_access_key));$signature =~ s/\n//g;

S3 上传策略

在构建表单之前,首先处理策略;这意味着您将在编写 HTML 之前确定您的安全性和可用性目标,这始终是一种可取的做法。

策略文档相当简单:

清单 2. 上传策略文档

{ "expiration": "3000-01-01T00:00:00Z", "conditions": [ {"bucket": "images.share.lifelogs.com"}, {"acl": "public-read"}, ["starts-with", "$key", ""], {"success_action_redirect": "http://share.lifelogs.com/s3uploaded/$user"}, ["content-length-range", 0, 1048576] ]}

S3 开发人员文档对所有内容进行了很好的解释。bucket 必须被命名,bucket 的 ACL 必须匹配,密匙可以以任何内容开头,成功后您将得到一个特殊的 URL。上传文档的大小被限制为 0 字节到 1 兆字节。注意过期日期(下面将详细介绍)。

策略的内容将被签名,并且是公共的,因此恶意用户很难伪造策略内容。这个属性可以确保站点的策略安全性 — 它确保根据特定的条件允许上传到 S3,在其他情况下则不允许上传。记住,您要为 S3 的使用付费,因此这非常重要。

过期日期被设置为 3000 年(是的,3000 年)。这样做的目的是使这个策略在所有实际使用中都不会过期。相反,您在以后可以将过期日期设置为 10 分钟,这样可以确保已删除的用户在超过最近一次合法访问的 10 分钟之后就不能再使用策略。但是这样设置的话,那些需要更长的时间上传文件的用户将被拒绝,他们将对此产生抱怨。因此,思考一下如何设置一个合适的过期日期,而不要随意设置它。

策略必须为表单中指定的所有字段设置条件。这将防止出现伪造并鼓励实现完整的策略文档。

我们已经确立了一个策略,现在让我们设置上传表单。

S3 上传表单

回忆一下,我们曾讨论过与 S3 对象相关联的 Content-Type 元数据,以及如何在上传对象前设置它。不幸的是,这不适合用于图像上传,因为无法提前获知用户要上传的内容。例如,JPEG 图像和 PNG 图像就使用不同的内容类型(它们实际上称为 MIME 类型,并且几乎有几百种常见类型)。

解决方法就是使用一些中等级别的 JavaScript 代码。由于您将所有内容都放入了 Perl 脚本,因此必须转义所有 \ 和 $ 字符(因此产生稍微有些混乱的内容)。查看实际 JavaScript 生成的 HTML;您将在本系列的后续文章中使用 Template Toolkit 完成该工作,但是它的中心思想是使脚本完整简单。它非常简单,但很可惜的是可读性受到一些影响。

清单 3. s3form.pl

#!/usr/bin/perluse warnings;use strict;use Data::Dumper;use Digest::HMAC_SHA1 qw(hmac_sha1 hmac_sha1_hex);use MIME::Base64;my $aws_access_key_id = 'get it from http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key';my $aws_secret_access_key = 'get it from http://aws-portal.amazon.com/gp/aws/developer/account/index.html?action=access-key';my $user = 'username'; # this is the user name for the uploadmy $policy = '{"expiration": "3000-01-01T00:00:00Z", "conditions": [ {"bucket": "images.share.lifelogs.com"}, {"acl": "public-read"}, ["starts-with", "$key", ""], ["starts-with", "$Content-Type", ""], {"success_action_redirect": "http://share.lifelogs.com/s3uploaded/$user"}, ["content-length-range", 0, 1048576] ]}';$policy = encode_base64($policy);$policy =~ s/\n//g;my $signature = encode_base64(hmac_sha1($policy, $aws_secret_access_key));$signature =~ s/\n//g;print <<EOHIPPUS;<html> <head> <title>S3 POST Form</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 submitUploaderForm(){ var form = \$('uploader'); // note the escapes we do from Perl var file = form['file']; var ct = form['Content-Type']; var filename = ''+\$F(file); // note the escapes we do from Perl 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? // note the escapes we do from Perl return m.type.length > 3 && m.type.match('/') && \$A(m.suffixes.split(',')).detect(function(suffix) { return f.match('\\.' + suffix.toLowerCase() + '\$'); // note the escapes we do from Perl }); }); if (type && type['type']) { ct.value = type.type; return true; } alert("Sorry, we don't know the type for file " + filename); return false;}</script> <form id="uploader" action="https://images.share.lifelogs.com.s3.amazonaws.com/" method="post" enctype="multipart/form-data" onSubmit="return submitUploaderForm();"> <input type="hidden" name="key" value="\${filename}"> <input type="hidden" name="AWSAccessKeyId" value="$aws_access_key_id"> <input type="hidden" name="acl" value="public-read"> <input type="hidden" name="success_action_redirect" value="http://share.lifelogs.com/s3uploaded/$user"> <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> <input type="submit" value="Upload File to S3"> </form> </body></html>EOHIPPUS

这段 JavaScript 代码有些难看,但是应当可以在大部分现代浏览器中正常工作。实际上,我们拦截了提交按钮,并且只有在知道所上传文件的类型的情况下才返回 true。

编者注:您可以 下载此脚本。清单 3 中粗体显示的两行脚本不应当像上面那样被打断,但我们的显示宽度有限。如果要从本文复制并粘贴此脚本,那么需要将这两行脚本恢复为一行代码。它们被正确放置在脚本中,您可以下载此脚本。

上传表单:JavaScript 和原理

您将从 Google API 站点加载 Prototype。如果您有多疑症或强迫症的话(您的多疑并不表示它们不存在),您可以自己托管它。

使用 Prototype 实用工具从表单获取文件名,然后查看文件名(使用小写)。对于每一个浏览器已知的 MIME 类型,使用 Prototype detect() 数组方法查找与以下条件的第一个匹配:

类型必须长于三个字符。 必须包含一个 / 字符。 MIME 类型的任何后缀都必须匹配文件名。

三字符检查和 / 字符检查是由于在 Firefox 中,(至少)有一个 “*” MIME 类型将匹配任何内容。由于没有什么用处,您希望能够跳过这些检查(我们也希望),并且任何其他 MIME 类型都不会影响我们要实现的目标。

您将使用 JavaScript split() 字符串方法遍历后缀,这将生成一个数组。因此,如果后缀为 jpg,jpeg,那么将遍历那些使用逗号分隔为两部分的内容。再一次,使用 Prototype detect() 数组方法在后缀中寻找与文件名的第一个匹配。比较小写形式的后缀和文件。

如果搞混了的话,再大致研究一下 Prototype 和 JavaScript。目前可以假设适合用于大多数情况。对于较旧的或不常见的 Web 浏览器可能不行,当然,如果用户禁用了 JavaScript 则肯定不能工作。现实就是这样。我们只能寻找适合大多数用户的方法。

如果类型声明失败,那么我们也会失败。尽管它将向用户显示一条消息,但是可以做得更好。比如,您可以尝试做一些猜测,如果不知道类型的话,那么可以回到以前的图像/jpeg。您可以做出一些改进。声明失败的话,函数将返回 false,这将阻断上传。

注意,对于成功的上传,您将被重定向到一个包含用户名的 URL。请参见 “上传到 S3” 理解这样做的原因。

最后,这个脚本将 Perl、JavaScript 和 HTML 混合到一个有趣的包(想像一辆扬着帆,掌着方向盘还吹奏着萨克斯的跑车)。为展示一种特殊技巧而编写的示例不应当作为您的设计和架构指导。我建议您不要完全复制和粘贴这里包含的脚本,至少应该考虑将它分解成模板片段并进行重构。在本系列后续文章中,我将向您展示如何在 mod_perl Web 站点的完整上下文中实现这些操作。

结束语

通过本文您了解了如何设置 HTML 上传表单将文件直接上传到 S3。我们用到了 Perl、JavaScript 和 HTML。本文给出了一个使用 Prototype JavaScript 库的脚本、Perl MIME::Base64 和 Digest::HMAC_SHA1 模块,以及内联的 JavaScript 和 HTML,并且提到一些注意事项。脚本将针对给定用户将文件上传到 S3,并最终重定向到 share.lifelogs.com 站点上的一个特定的有效 URL。

本系列的第 3 部分将展示这个有效的 URL 如何为上传的文件创建 SimpleDB 记录。您还将发现对于特定用户,如何以 SimpleDB 记录的形式创建、编辑和删除某张照片的评论。第 4 部分和第 5 部分将为您组装 mod_perl Web 站点,请继续关注。

本文的示例脚本,s3form.zip:
temp_10030721378776.zip

时间: 2024-10-16 19:18:09

功能丰富的 Perl: Perl 和 Amazon 云,第 2 部分--通过 HTML 表单将数据安全地上传到 S3的相关文章

功能丰富的 Perl: Perl 和 Amazon 云,第 4 部分--深入探究完整 mod_perl 站点的代码库

这个共分 5 部分的系列文章向您介绍了如何使用 Perl 和 Apache 构建一个照片共享网站,从而访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB.在本期文章中,研究完整 mod_perl 站点的代码库,包括如何设置顶级配置.如何使用处理程序,以及如何设置外部依赖关系. 在本期文章中,我们将研究完整的 mod_perl 站点(只讨论代码:模板在下期文章讨论).前几期文章中,我们的节奏有些缓慢,现在,通过研究 mod_perl,我们将加快步伐

功能丰富的 Perl: Perl 和 Amazon 云,第 5 部分--了解完整 mod_perl 站点的模板

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

功能丰富的 Perl: Perl 和 Amazon 云,第 3 部分--上传图像并创建、编辑和删除评论

这个共分 5 部分的系列文章向您介绍了如何使用 Perl 和 Apache 访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB,从而构建一个照片共享网站.在本期文章中,通过学习 URL 如何为上传的文件创建 SimpleDB 记录,实现站点与 SimpleDB 的交互.同时了解如何以 SimpleDB 记录的形式创建.编辑.删除某个用户的照片评论. 距离我的上一期文章已经有一段时间了,现在我们来回顾一下: 第 1 部分解释了 S3/SimpleD

功能丰富的 Perl: Perl 和 Amazon 云,第 1 部分--通过构建简单的照片共享网站学习 Amazon S3 和 SimpleDB 服务的基础知识

在这个 5 部分系列文章的第 1 部分中,Ted Zlatanov 解释了 Amazon 的 Simple Storage Service (S3) 和 SimpleDB 产品在构建 Web 站点方面的优势和不足.这个系列通过实际示例形象地介绍了 S3 和 SimpleDB 架构以及如何使用它们:在本文中,将使用 Perl 库构建一个简单的照片共享网站. 您需要学习 Amazon 的两个 Web 服务:Amazon S3 (Simple Storage Service) 和 Amazon Sim

教程:如何将Linux应用程序迁移到Amazon云

基础设施即服务(Infrastructure as a Service,IaaS)是一个极好的概念:您使用计算资源并支付费用.需要的资源越多,支付的费用也就越高.这个模式的不足之处在于,您看不到正在使用的计算机,您对那些计算机真的知之甚少.但一旦克服这一点,使用 IaaS 可以带来很多好处. 由于 IaaS 模式与传统的购买服务器的模式差别很大,因此您管理您的虚拟计算机的方法也随之发生改变.这还意味着,您在云中运行应用程序的方式也发生了改变.以前想当然的东西,比如服务器之间无足轻重的延迟,现在不

Amazon云基础设施采用英特尔芯片

Amazon公司坦言称,目前其云基础设施使用的正是英特尔的芯片.这一声明解开了长久以来困扰技术行业的猜测与疑惑. 这条声明来自本周二于旧金山召开IDF(Intel Developer Forum),此次Amazon史无前例地就其云基础设施处理器问题大谈特谈,并从细节角度重点强调了英特尔产品的卓越功能. Amazon表示引入英特尔芯片的做法是为了满足客户需求,而英特尔方面则指出这项协议是为了推动Amazon云的品牌号召力更上一层楼.不过面对我们提出的问题--AWS对芯片产品的批量性采购是否能获得来

报告称,Amazon云服务成为史上增长最快的企业级

摘要: 美国投行太平洋皇冠证券(Pacific Crest Securities)的最新数据显示, 2014年,Amazon云计算服务(Amazon Web Services, AWS)截至目前的收入已经达到了50亿美元. 这意味着Amazon来自其云计算服务 美国投行太平洋皇冠证券(Pacific Crest Securities)的最新数据显示,2014年,Amazon云计算服务(Amazon Web Services, AWS)截至目前的收入已经达到了50亿美元.这意味着Amazon来自其

HTML 5之表单新功能解析

时光车轮滚滚碾来,前端之路永无止歇.对于这个前端这门精一多专的技术,任何一次技术革新,我们都必须第一时间去了解它学习它,比如Web世界里这簇美艳的花朵---HTML 5.虽然HTML 5发布之初,许多人(包括我)都觉得普及它还很遥远,但自发布以来,许多企业级网站对它的尝试应用(比如<!doctype html>应用,比如canvas的应用),使HTML 5的迈出了一大步,随之而来IE9的发布,无疑又让我们看到了HTML 5离我们不再遥远. 51CTO推荐专题:HTML 5 下一代Web开发标准

实现多个WEB页面要接收同一个表单的数据的功能

web|数据|页面 有时候我们会遇到这样的问题,即多个页面要接收同一个表单的数据,那么怎么实现这一功能呢,本文将会阐述. 我们知道,一个表单可以很容易地递交到一个处理页面,只需要一个submit按钮即可,但有时候,我们要用不同的处理页面来接收表单的数据并加以处理.如果我们为不同的处理页面分别设计各自的表单的话,如果采集的数据都是一样的,在这种情况下既浪费页面篇幅同时在大数据量输入时非常浪费时间和精力.要解决这一问题我们只需要实现一个表单向多个页面递交数据这一功能即可. 那么怎么实现这一功能呢?下