この記事は 今年もやるよ!AWS Lambda縛り Advent Calendar 2015 の23日目です。

「何か書くぞ!」と思いエントリーしてみたもののどうしよう、、と思っていたのですが、そういえば個人的に Apache Tika というのが最近気になっていたので使ってみることに。

Apache Tika

これは、PDFやエクセル等のファイルから、テキストデータやメタデータを抽出してくれるという便利ライブラリです。これを使って、ファイルが登録されたら裏でテキストデータを抽出して、それを全文検索で引っかかるようにする…みたいなことが出来ると楽しそうです。

Apache Tika自体はJavaのライブラリなのですが、Node.jsから扱うためのライブラリ があったので、今回はこれを使って

1. S3にPDFファイルが置かれる
2. それをトリガーとしてLambdaが起動し、Apache Tikaを使ってテキストデータを抽出する
3. 抽出したテキストデータをS3に置く

という流れをやってみたいと思います。

Lambdaを触ってみる

では早速、AWSのマネジメントコンソールからLambdaを触ってみましょう。

(クリックで拡大されます)
Step1

今回はS3にファイルが置かれたのをトリガーとしてLambdaを起動したいので、"s3" というキーワードでフィルタリングした状態です。Node.jsである一番右側の「s3-get-object」を選びました。
※ちなみにblueprintは、青写真とか設計図とかいう意味

次は、event source(トリガー)の指定です。今回はPDFファイルの作成や更新時にのみ、Lambdaを起動させるようにしました。

Step2

続いて、Lambda起動時に実行される処理を登録します。今回は10MBを超える容量となったので、S3に必要なファイルをまとめてzipにしたものを置いておき、それを取り込むようにします(後述します)。

Step3

LambdaにはIAMのロールを設定する必要があるので適切なロールを設定してください。今回はS3へのアクセスができるようになっていれば良いかと思います。

Step4

トリガーの反映は後で良いでしょう。基本的にはテストデータで動作確認して、動くようになったらトリガーをONにし、実際にS3にファイルが置かれたことをトリガーとしてLambdaが起動するようにします。

テキストデータの抽出

さて、いきなり結論となってしまいますが、今回はこんなコード(index.js)を書きました。

console.log('Loading function');

var aws = require('aws-sdk');
var s3 = new aws.S3({ apiVersion: '2006-03-01' });

var tika = require('tika');

exports.handler = function(event, context) {
    console.log('Received event:', JSON.stringify(event, null, 2));

    var bucket = event.Records[0].s3.bucket.name;
    var key = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    var params = {
        Bucket: bucket,
        Key: key
    };
    s3.getObject(params, function(err, data) {
        if (err) {
            console.log(err);
            var message = "Error getting object " + key + " from bucket " + bucket +
                ". Make sure they exist and your bucket is in the same region as this function.";
            context.fail(message);
        } else {
            console.log('CONTENT TYPE:', data.ContentType);
            console.log('KEY:', key);

            var url = "https://s3-" + event.Records[0].awsRegion+ ".amazonaws.com/" + bucket + "/" + key;
            console.log(url);

            // テキストデータの抽出
            tika.text(url, function(err, text) {
                if (err) {
                    context.fail(err);
                } else {
                    s3.putObject({
                        Bucket: bucket,
                        Key: key + ".txt", // S3にUPするファイル名
                        Body: text
                    }, function(err, data) {
                        if (err) {
                          context.fail(err);
                        } else {
                          context.succeed("Successfully finished");
                        }
                    });
                }
            });
        }
    });
};

テキストデータの抽出のところ、びっくりするくらい簡単に出来るので拍子抜けです。ただこれ、最終的にはLambda上で動かせてません。。(ローカルでは動くんですが><)

なお事前に、

$ npm install aws-sdk --save-dev
$ npm install tika --save-dev

は実行済みです。その後、index.jsとnode_modulesをzipに固めて、S3に置いてLambdaの起動時の処理として登録しています。最終的に50MBほどのサイズになってました。

$ zip -r extract_text_data.zip index.js node_modules

S3のバケットポリシー

今回はLambdaからS3に置かれた任意のファイルにアクセスしてテキストデータの抽出をするので、バケットごと誰でも読み取りができるようにしています。。><

S3のバケットポリシー

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "AddPerm",
			"Effect": "Allow",
			"Principal": "*",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::<bucket_name>/*"
		}
	]
}

<参考URL>
S3のアクセスコントロールが多すぎて訳が解らないので整理してみる | Developers.IO


・・さて、そんな感じで結局はうまくいっていないんですが、一応遭遇した問題を残しておきます。m(_ _)m

遭遇した問題その1

トリガーをONにしようとしたら、

There was an error creating the event source mapping: Configuration is ambiguously defined. Cannot have overlapping suffixes in two rules if the prefixes are overlapping for the same event type.

こんなエラーが出てきてトリガーがONにできず困りました(どういうことなの???)。でも次の日またやってみたらいけた。なんだったんだろうか。。

遭遇した問題その2

Lambdaで実行していたら、invalid ELF header というエラーが!!どうも調べてみると、LambdaはLinux上で動作するっぽいんですが、Macでコンパイルしているため、Linux上でそれが正しく動かないのが問題のようです。

解決策としては、AWSのAmazon LinuxのEC2上でコンパイルしようという話のようなので、そりゃそうだと思いつつ実際に試してみました。が、それを使うと今度はこんなエラーが。。(解決できず)

libjvm.so: cannot open shared object file: No such file or directory

結局は動かせてないし時間が無くてバタバタなんですが、何かの参考になれば幸いです。
このエントリーをはてなブックマークに追加