0

phpでS3を扱うamazon-s3-php-class

今回やりたかったのは、あまりアクセスされなくなったメディアファイルを現サーバーからS3にバックアップさせるというやつです。
ファイル数が1万弱あるので手動では厳しいしいのでスクリプトに頼ることにしました。

やりたいことは、
ディレクトリ構成は極力保持。
DBで削除フラグがたってるようなやつは移動させない。
なので、そのままディレクトリを同期っていうやつは使えません。
WEBサービスの中でユーザーがアップロードしたらそのままS3へ同期ってのをいずれやりたいので、できればPHPで。

そんな条件で探してたら、洋物ながらamazon-s3-php-classっていうのがありました。
CURLを使ってHTTPメソッド(GET、PUT、DELETE、POST)なんかができます。

付属のサンプルを動かしてみたらうまくいったのだけど、どうも2階層目以降のディレクトリが指定できない。
(そもそも2階層目以降はディレクトリと呼ぶのか?これらもバケット?)
http://hoge.s3.amazonaws.com/huga.jpgだといけるけどhttp://hoge.s3.amazonaws.com/file/2008/huge.jpgだとうまくエラー吐き出してうまくアップロードができません。

putObjectFileのソースを追っていくと第2引数がそのままhttp://(第2引数).s3.amazonaws.comとしてサブドメインとして使われてるのが原因でした。

下記のようにS3.phpを修正したらうまくいくようになりました。
但し今回使うputObjectFileだけ。
// 1093行目あたり
$this->headers['Date'] = gmdate('D, d M Y H:i:s T');
$this->response = new STDClass;
$this->response->error = false;
 
//以下を追記
if (strstr($this->bucket, '/')) {
$tmp_bucket = explode("/", $this->bucket);
$subdomain = $tmp_bucket[0];
$this->headers['Host'] = $subdomain . '.' . $defaultHost;
 
$tmp = array_slice($tmp_bucket, 1);
$this->directory = implode("/", $tmp);
}

//1143行目あたり
if (sizeof($this->parameters) > 0) {
$query = substr($this->uri, -1) !== '?' ? '?' : '&';
foreach ($this->parameters as $var => $value)
if ($value == null || $value == '') $query .= $var.'&';
// Parameters should be encoded (thanks Sean O'Dea)
else $query .= $var.'='.rawurlencode($value).'&';
$query = substr($query, 0, -1);
$this->uri .= $query;
  
if (array_key_exists('acl', $this->parameters) ||
array_key_exists('location', $this->parameters) ||
array_key_exists('torrent', $this->parameters) ||
array_key_exists('logging', $this->parameters))
$this->resource .= $query;
}
 
//以下を追記
if (isset($this->directory)) {
$this->uri = '/' . $this->directory . $this->uri;
}

これだと、putObjectFileの第2引数はhoge/file/2008/と指定できます。
DELETEのとこも同様な処理をしたらいいんだろうけど、とりあえずは必要なとこだけ修正して使ってます。

こんな感じで動いてるけどS3遅い。
遅すぎる。

総ファイル数9300、総ファイル容量15Gをアップ中だけど、ISDN並の速度。
この調子だと4日くらいはかかりそう…