はじめに
Lambdaでは、Amazon S3にコンテンツがPUTされると、コードを実行することができます。
そこで、S3にファイルがPUTされたら、そのバケットにあるコンテンツの一覧を表示する「index.html」を自動生成してみたいと思います。
動作結果は、下の図のとおり。S3においてあるコンテンツの内容が一覧表示される、簡易なコンテンツ共有サイトを簡単に実現できます。ファイル一式を友人に渡す時に便利ですね。ファイルのLinkは、署名付きURLにして期間限定公開にすることや、AWSのマネージメントコンソールで「make public」されたのと同じ状態で、期間限定なしでダウロード可能な状態にすることもできます。
余談ですが、この「make public」ってメニュー項目の響きが好きです。これで、世の中に公開されるんだな、という感じです。「Work smart, Have fun, Make public」という感じです。
Lambdaコード
このlambdaコードは、Oregonリージョンで動かします。
console.log('Loading event');
var AWS = require('aws-sdk');
var s3 = new AWS.S3();
var regionurl = 'https://s3-us-west-2.amazonaws.com/';
exports.handler = function(event, context) {
var htmlheader = '<html><haed><link href="test.css" type="text/css" rel="stylesheet" /></head><body><table class="sample007"><tr><th>No.</th><th>Contents</th><th></th><th>Date</th></tr>';
// index.html header
var htmlfooter = '</table></body></html>';
// index.html footer
var indexhtmlbucket = 'yoicht-makepublic'; // bucket of Static web hosting
var htmlfilename = 'index.html'; // filename of index document
var maxkeys = 100; // max keys
var bucketname = event.Records[0].s3.bucket.name;
var bucketkey = event.Records[0].s3.object.key;
console.log('Bucket = ' + bucketname);
console.log('key = ' + bucketkey);
var param = {
Bucket: bucketname,
Key: bucketkey,
ACL: 'public-read' // If signed-url, write 'private'.
};
s3.putObjectAcl(param, function(err, data) {
if (err)
console.log(err, err.stack);
else
makeTable(bucketname, htmlheader, htmlfooter, indexhtmlbucket, htmlfilename, maxkeys, param.ACL );
});
};
function makeTable(bucketname, htmlheader, htmlfooter, indexhtmlbucket, htmlfilename, maxkeys, acl ) {
var params = {Bucket: bucketname, EncodingType: 'url', MaxKeys: maxkeys};
var url ='';
s3.listObjects(params, function(err, data) {
if (err) {
console.log(err, err.stack);
} else {
var indexhtml = htmlheader;
for(var index in data.Contents) {
if (acl == 'public-read') {
url = regionurl + bucketname + '/' + data.Contents[index].Key; // for make public
} else {
params = {Bucket: bucketname, Key: data.Contents[index].Key, Expires: 36000};
url = s3.getSignedUrl('getObject', params);
}
indexhtml += '<tr><td align="center">'+ index + '</td>' +
'<td><a href="' + url + '">' + data.Contents[index].Key + '</a></td>' +
'<td><img src="' + url + '" height="50">' + '</td>' +
'<td>' + data.Contents[index].LastModified + '</td>' +
'</tr>';
}
indexhtml += htmlfooter;
params = {
Bucket: indexhtmlbucket,
Key: htmlfilename,
ACL: 'public-read',
Body: indexhtml,
ContentType: 'text/html'
};
s3.putObject(params, function(err, data) {
if (err)
console.log(err, err.stack);
else
console.log(data);
});
}
});
console.log(indexhtml);
context.done(null, "done");
}
var param = {
Bucket: backetname,
Key: backetkey,
ACL: 'public-read'
};
で、ACL : 'private'と書けば、アクセス期間を限定可能なsigned-urlになります。
注意点が2つあります。
ひとつめは、「index.html」を生成するバケット(「indexhtmlbucket」)は、コンテンツをPUTするバケットとは別にしておく必要があります。このコードを流用される場合は、ご自分のBucketに変更する必要があります。また、Bucketは、Static Web Hostingの設定をしておく必要があります。
ふたつめは、Lambdaの「Change function configuration and role」の設定で、S3の権限設定を変える必要があります。デフォルトのRoleで設定されているのは、S3の読み込みと書き込み権限だけです。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:*"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::*"
]
}
]
}
動作の概要
- S3 PUTをトリガーに、Lambaのコードが実行開始。
- S3 PUTされたコンテンツのバケット名とkeyを取得。
- S3 PUTされたコンテンツのアクセス権を設定(public-read or private)。
- バケット内のコンテンツの一覧を取得。
- tableタグでコンテンツ一覧を整形。
- index.htmlを作成し、static web hostingの設定がしてある別のバケットにPUT。
- おしまい。
さいごに
このコードは、改良の余地が多くあります。まず、削除が反映されません。これは、Lambdaで削除イベントのきっかけをつかめないためです。あと、処理をサボっているので、日本語のコンテンツに対応できていないなどがあります。
そのほか、S3 PUTをきっかけに、コンテンツのサムネイルを自動生成したり、署名付きURLのダウンロードできる期間もあわせて表示したり、styleシートを読み込み見た目リッチなhtmlにする、など思いつきました。
Lambdaを使うのは初めてでしたが、簡単ですね。Lambdaを使うと、S3のstatic web hostingも、「static」が取れてしまいます。便利!。
この記事は、私の所属している組織とは何の関係もありません。