使用 AWS S3 和 Paws 部署静态网站

Amazon Web Services (AWS) 是亚马逊的云服务平台,S3 是 AWS 文件存储服务。S3 通常用于托管静态网站。使用 Perl,我们有许多模块用于使用 AWS,但我喜欢由 Paws 开发者 Jose Luis Martinez 开发的模块,它支持许多 AWS 服务,包括 S3。在这篇文章中,我将向您介绍我开发的用于使用 S3 和 Paws 上传和维护静态网站的 Perl 脚本。

AWS 配置

要从命令行使用 AWS,您需要为您的账户生成一个密钥 ID 和秘密密钥,您可以从 AWS 网站 获取。一旦使用您的亚马逊凭证登录,点击您的账户名称,转到“我的安全凭证”。一旦您有了密钥 ID 和秘密密钥,您需要创建 awscli 所使用的凭证文件。您可以选择安装 awscli 并运行 aws configure,否则创建

~/.aws/default:
[default]
output = JSON
region = us-east

~/.aws/config:
[default]
aws_access_key_id = XXXXXXXXXXXX
aws_secret_access_key = xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

将区域值更改为您想要使用的 AWS 区域,并将“XXX”值替换为您自己的密钥 ID 和秘密密钥值。这些文件在 Windows 上的存储位置不同。

创建 S3 存储桶

S3 通过存储桶组织文件。每个存储桶都有一个类似于 URI 的名称,在整个 AWS 中是唯一的。因此,如果您要在 S3 上托管网站,您需要为网站创建一个存储桶。这可以通过 AWS Web 界面、命令行 应用程序 或使用 Paws 完成

use Paws;
my $s3 = Paws->service('S3', region => 'us-east-1');
$s3->CreateBucket(Bucket => 'mystaticwebsite.com', ACL => 'public-read');

ACL 参数指定存储桶可以公开读取,但不能编辑,这对于网站文件来说是合理的。在某个时候,您需要为存储桶启用“静态网站托管” 选项,但这不是上传文件到存储桶所必需的。

上传文件到 S3

S3 文件以对象的形式存储在存储桶中。每个文件都有一个键,它类似于文件名。我开发了一个名为 s3-upload 的脚本,它使用 Paws 将文件上传到 S3 存储桶。它使用 Getopt::Long 解析命令行选项。它需要 --bucket 作为 S3 存储桶名称、--region 作为 AWS 区域以及 --files 作为目录文件路径

#!/usr/bin/env perl
use Getopt::Long 'GetOptions';
use Paws;
use Path::Tiny 'path';

GetOptions(
  'bucket=s'     => \my $BUCKET,
  'files=s'      => \my $BASEPATH,
  'region=s'     => \my $REGION,
  'delete-stale' => \my $DELETE_STALE,
) or die 'unrecognized arguments';

die 'must provide --bucket --region --files'
  unless $BUCKET && $REGION && $BASEPATH;

die "directory $BASEPATH not found" unless -d $BASEPATH;

my $s3             = Paws->service('S3', region => $REGION);
my $remote_objects = get_remote_objects($s3);
my $local_objects  = upload($s3, $remote_objects);

delete_stale_objects($s3, $remote_objects, $local_objects) if $DELETE_STALE;

为了简洁起见,我省略了子程序的定义(有关详细信息,请参阅 源代码)。该脚本首先验证输入选项,然后创建一个 $s3 对象。它调用 get_remote_objects,该函数返回一个键(文件)及其在存储桶中的最后修改时间的散列引用。它将此传递给 upload,该函数仅上传自上次上传到 S3 以来已修改的文件(如果只有一个文件已更改,则不需要上传整个网站)。upload 执行许多操作,但本质上,它使用 PutObject 上传文件

sub upload {
  ...
  $s3->PutObject(
    Bucket  => $BUCKET,
    Key     => $key,
    ACL     => 'public-read',
    Body    => $path->slurp_raw,
  );
  ...
}

在这里,Key 是文件名,而 Body 是文件的原始字节数据。upload 子程序还返回一个本地键及其最后修改时间的散列引用。可选地,该脚本可以调用 delete_stale_objects,该函数会删除 S3 中不存在的本地树中的文件。

该脚本可以像这样运行

$ ./s3-upload --bucket mystaticwebsite.com --region us-east-1 --files mywebsite/static --delete-stale
static/index.html
static/about.html
static/news.html
static/products.html
static/css/styles.css

该脚本将打印上传到 STDOUT 的任何文件,并将所有其他输出打印到 STDERR。目的是使能够将上传的文件名管道传输到其他程序。一个有用的程序可能是 Cloudfront 脚本,它将任何上传的文件的缓存失效。

更多功能

尽管上面的脚本可以完成工作,但还有一些对于静态网站有用的功能缺失。首先,您可能希望指定正在上传文件的MIME类型。这样,当浏览器获取文件时,S3会响应正确的内容类型头信息。否则,HTML文件可能无法显示为网站,图像可能会被下载而不是显示,等等。我使用Media::Type::Simple来完成这项工作

use Media::Type::Simple;
...
# setup mime types, add missing
open my $mime_types, '<', $MIME_TYPES or die "Can't find $MIME_TYPES $!";
my $media = Media::Type::Simple->new($mime_types);
$media->add_type('application/font-woff2', 'woff2');
...
my @ext  = $path =~ /\.(\w+)$/;
my $mime = eval { @ext ? $media->type_from_ext($ext[0]) : undef };
print STDERR $@ if $@;

我已经将mime.types的一个副本上传到仓库,并为文件路径添加了--mime-types选项指向mime.types文件(默认为/etc/mime.types)。另外,并不是所有媒体类型都被定义,因此代码为woff2添加了自定义定义。文件上传时将MIME类型传递给PutObject

脚本支持的其他有用选项

  • --strip - 访问/home似乎比访问/home.html更简洁。可以使用--strip选项指定从文件名中删除的任何扩展名
  • --max-age - 设置缓存控制头信息,以便浏览器缓存文件,而不是在每次访问页面时下载它们
  • --force - 覆盖默认行为并上传所有文件,无论它们是否已在S3存储桶中存在

这些选项可以使用如下方式使用

$ ./s3-upload --bucket mystaticwebsite.com --region us-east-1 --files mywebsite/static --delete-stale --mime-types mime.types --strip html --max-age 31536000 --force
static/index.html
static/about.html
static/news.html
static/products.html
static/css/styles.css

该脚本的源代码位于GitHub上。如果您需要帮助配置AWS的静态网站,Amazon提供了一个很好的指南


本文最初发布在PerlTricks.com上。

标签

David Farrell

David是一位专业的程序员,他经常推文博客关于代码和编程艺术。

浏览他们的文章

反馈

这篇文章有什么问题吗?请通过在GitHub上打开一个问题或拉取请求来帮助我们。