PHPからAmazon S3を利用するライブラリを3つ試してみた(うち一つは動作不可)
Amazon Simple Storage Service(s3)がCDNに対応して日本へも高速に配信できるようになったので、日本国内でS3の注目が高まっているかと思います。
ヨセミテでもS3の利用を検討しようということで、まずはphpからライブラリ経由でS3を試してみました。
使ってはいけない「s3.class.php」(2009-01-07時点)
PHP を使ってマルチメディアのコンテンツと帯域幅を Amazon に任せる (IBM developerWorks) で配布されているs3.class.php(アーカイブ名はos-php-amzmm.s3class.zip)と、記事中のサンプルコード(のbuket名を変更したもの)を設置し、upload.phpからファイルをPUT(アップロード)してみました。そして、S3 Organizer で確認したところ、ファイルは確認できませんでした。
原因調査のため、サンプルコード中でbucketを作成している部分である「$srvc->putBucket()」の返り値を出力してみるとawsから以下のエラーが返ってきていました。
<Error>
<Code>AccessDenied</Code>
<Message>
AWS authentication requires a valid Date or x-amz-date header
</Message>
<RequestId>25***740030BFE51</RequestId>
<HostId>
4tTJ+Z53jwlVD*********MpCT6CAV3pl/aa6fbCi+cik6cC2spbBuFargcNj3J+
</HostId>
</Error>
これはDateヘッダの形式が正しく無いためで、s3.class.php内の
$this->httpDate = gmdate(DATE_RFC822);
を
$this->httpDate = gmdate(DATE_RFC1123);
に変更することで正しい形式になります。これは自力で修正できました。
が、しかし、次のエラーで挫折しました。
<Error>
<Code>SignatureDoesNotMatch</Code>
<Message>
The request signature we calculated does not match the signature you provided. Check your key and signing method.
</Message>
<StringToSignBytes>
50 55 54 0a 0a 0a 57 65 64 2c 20 30 37 20 4a 61 6e ** ** 30 30 39 20 30 34 3a 32 39 3a 31 38 20 2b 30 30 30 30 0a 2f 64 65 76 2e 6f 6e 6c 69 66 65 2e 34 34 67 2e 62 69 7a
</StringToSignBytes>
<RequestId>5FA***75CAF329E6</RequestId>
<HostId>
uOuUqle1Iv/Ie*********Q/To1DgMM3nCr6PIZFJdLZpEQ/WX4ACXrjk1zX1Gxg
</HostId>
<SignatureProvided>ma/GIbpv******xr/q5sPpRqPg=</SignatureProvided>
<StringToSign>
PUT
Wed, 07 Jan 2009 04:29:18 +0000
/dev.******.biz
</StringToSign>
<AWSAccessKeyId>0G***************Z82</AWSAccessKeyId>
</Error>
「SignatureDoesNotMatch」をベースに検索したら沢山ヒットしたのですが、s3.class.phpを直したという情報は見つけられず、原因も奥が深そうなので諦めました。
原因はおそらくコードのバグかAmazonの仕様変更でしょう。最初に試したのがコレだったので、原因がコードのバグか自分の手順ミスかどうかがはっきりせず、1時間程度調査に費やされました。検索で上位に来やすい記事なので同じように悩んでいる人も多いでしょう。それがこの記事を書くきっかけとなっています。
Cloud Frontにも対応した「amazon-s3-php-class」
なかなか検索にヒットしなくて諦めかけてたところに見つけたのが Undesigned: Amazon S3 PHP Class で配布されている amazon-s3-php-class - Google Code でした。
こちらをサンプルコード通りに使ってみたところ、問題なく成功しました。
<?php
rqruie_once 'S3.php';
$s3 = new S3('accessKey', 'secretKey');
$s3->putBucket('bucket', S3::ACL_PUBLIC_READ);
$s3->putObjectFile('file.doc', 'bucket', 'docs/file.doc', S3::ACL_PUBLIC_READ);
amazon-s3-php-classは現在進行形でこまめにアップデートも行われており、最終更新日は2008-12-23となっています。なんとCloud Front用の機能も実装されているようです。
めでたく利用検討対象となりました。
PEAR::Services_Amazon_S3もalphaだけど良さそう
amazon-s3-php-classを見つけて一安心したところで、「そういえばPEARにあったりして」と思って検索してみたら、ありました。「PEAR :: Package :: Services_Amazon_S3」です。こちらもサンプルどおりに試してみたところ、正常動作しました。
<?php
require_once 'Services/Amazon/S3.php';
$accessKeyId = 'accessKey';
$secretAccessKey = 'secretKey';
$s3 = Services_Amazon_S3::getAccount($accessKeyId, $secretAccessKey);
foreach ($s3->getBuckets() as $bucket) {
echo "<li>" . htmlspecialchars($bucket->name) . "</li>";
}
どうやら2008-04-07に登録されたパッケージで、まだalpha版なので pear install --force Services_Amazon_S3 で強制インストールしないと導入できません。また、Cloud Front用の機能も無いようです。
結構使いやすい感じがしたので、こちらも利用検討対象としました。
Cloud Front用の機能がありませんが、現時点ではphpからCloud Front用の設定はphpからは多分行わない気がします。というのも、Cloud Frontは最初に配信用の独自ドメインを設定すれば、配信に関してはあとはやることがないのです。phpから動的にbucketを作成したいなら別ですけどね。
IBMさんのコードはS3の紹介記事に付属するサンプルコードである
ちょっとIBMさんをDisるように見える記事になってしまいました。ただ、IBMさんのコードはライブラリ然としていますが、そもそもS3の紹介記事とそのサンプルコードなので、コードの保守を期待する方が贅沢というものです。記事とコード自体はすごく分かりやすくて参考になりましたしね。IBMさん、ありがとうございました!
[...] original article [...]
s3.class.phpを以下に変更したら動いたと思うので、一応書いてみます。
修正前:
$contenttypestring = $contenttypestring.",application/x-www-form-urlencoded";
修正後:
$contenttypestring = $contenttypestring;
でもCloudFront対応で更新されているamazon-s3-php-classの方が断然良いですね^^;