てくてくテック

【Laravel】動画等の大きなファイルをS3から取得したい!

どーも!

たかぽんです!

今回はS3から動画ファイル(500MB以上など...)をlaravelで取得したお話をします。

以外にもそれについての記事が少なくて、調べるのが大変でした...orz

ただ、先人があまりしていないことをやるのはちょっと楽しかったりしますね!

やりたかったこと

さて、ではまず最初にやりたかったことについてです。

やりたかったのは、AWSのS3にある動画のような大きめのファイルを別のサービスに対してアップロードする。

ということでした。

最初は特になにも考えず、GetObjectでやろうとしました。

$result = $s3->getObject([
  'Bucket' => 'bucket',
  'Key'    => 'key'
]);

すると...

PHP Fatal error: Allowed memory size of xxx  bytes exhausted

作業メモリたりねぇぞぉ!

って怒られるわけですよ...orz

大抵画像のアップロードなんかはうまくいくんですが、動画は...甘く...ねぇ...

仮に作業メモリ上限を高く設定して成功したとしても、明らかにリソースの無駄ですし、オススメできる方法ではない...

直接S3からどうにかすればアップロードできるかも?しれませんが、ちょっと方法に皆目検討もつかず断念...

というわけで、一旦ローカルのディスクに保存をして、そこから別のサービスへアップロードをするような方針でやってみました。

では、早速みていきましょう。

S3からサイズの大きなファイルをlocalへ取得する

さて、ではS3からサイズが大きいファイルを取得するんですが、こう言う場合、laravelのfilesystemのreadStreamというメソッドを使っていくのがよさそうです。

このstreamを使えば大きなファイルをdiskからdiskへコピーができるとのこと。

では、実際に僕が書いたものをみてみます。

$uniqueTmpFileName = (string) Str::uuid();
$inputStream = Storage::disk('upload')->getDriver()->readStream($key);
Storage::disk('uploadtmp')->getDriver()->writeStream($uniqueTmpFileName, $inputStream);
// ...
// 動画に対する処理(別サービスへのアップロード等)
// ...
Storage::disk('uploadtmp')->delete($uniqueTmpFileName);

今回、行いたいことはローカルに一旦保存して、アップロードを別のサービスに対して行いたかったので...

ローカルに保存するファイルは処理が終わったら削除します。

なので、最初に一意な一時ファイル名(ローカルに保存されるファイルの名前)をuuidを使って生成しています。

(わざわざ一意にする必要はないかも?ですが、将来的に並列化したい!等になった場合も考慮してこうしています。)

その後、普通に行うとだめなので...

s3からキーを指定し、readStreamすることで$inputStreamとしてresource?を取得します。

そのresourceをlocalにファイルとして保存するわけですが、保存する際はwriteStreamとして第一引数にローカルでのファイル名、第二引数に保存したいデータのストリームを渡します。

これで、localに一意な名前で一時的にファイルを保存することができます。

(ここら辺はまだ理解浅めです...orz)

その後、そのlocalのファイルを指定して外部へアップロードするAPI等を叩いたりします。

そして全ての処理が終わったら、一生localに溜め続けるとdiskを圧迫してしまうので、不要な一時ファイルをdeleteメソッドで削除します。

上記が一連の操作になります。

これで、とりあえず大きめのファイルでもローカルへ持ってくることができ、そのファイルを使って別サービスへアップロードできました!

ちなみに、diskの設定は例えば以下のような形です。

// filesystem.php
'uploadtmp' => [
    'driver' => 'local',
    'root' => storage_path('app/public/tmp'),
    'url' => env('APP_URL').'/storage',
],
'upload' => [
    'driver' => 's3',
    'key'    => env('AWS_ACCESS_KEY_ID'),
    'secret' => env('AWS_SECRET_ACCESS_KEY'),
    'region' => env('AWS_DEFAULT_REGION', 'ap-northeast-1'),
    'bucket' => env('AWS_S3_BUCKET_UPLOAD'),
],

uploadtmpは一時的にファイルを保存するローカルの場所について、そしてuploadは持ってきたいデータが保存されているS3のバケットの情報になっています。

各自好きなパスに書き換えてください。

それぞれ.envの設定も忘れないようにしましょう!

まとめ

今回は大きなファイルをS3から取得する方法について書いてみました。

結構悩み抜いたんですが、readstream...また新しいことをしれましたね...!

完全に理解できていないので。時間があればもうちょっとストリームとはなんぞや?とか調べてみたい。

またしばらく先にはなりそうですが...w

S3から大きなファイルを取得してきている他のサービスとかどうしてるんだろ...?

おんなじ感じなのだろうか...

まぁ、無事できたと言うことで!

それでわ!

モバイルバージョンを終了