はじめに#
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」が取れてしまいます。便利!。
この記事は、私の所属している組織とは何の関係もありません。
