【Laravel】AWSのS3へデータをアップロードするためのPresignedURLを作る方法

どーも!

今回はAWSにアクセスするためのPresignedURLを作っていきます!

初見でpresignedURL?と言われてもはてなまーくですよね...w

僕もそうでした!

なので、そこから解説していきたいと思います!

PresignedURLって?

さて、AWSにはS3というバケットが存在しています。

簡単に言うとクラウドサービス的なものと捉えてもらっていいです。

画像や動画、テキストファイルなど、様々なファイルをアマゾンのクラウド上に保存して、手元のサービスからあーだこーだして持ってきたり、保存したり色々できるわけですね。

そのS3にデータを置くためには一般的にサービス側でcredentials(認証情報)、例えばAWSアクセスキーやAWSシークレットアクセスキー等(俗に言うIAM Userとして)使ってアクセスする必要があります。

ただ、場合によってはその権限を持っていない人にもアクセスやアップロードを許可したい場合があります。

それを一時的に可能にするURLがPresignedURLです!

ちなみに署名付きURLなんて呼ばれたりもします。

それでは早速コードをみていきましょう。

PresignedURLを作ってみる。

今回、Laravelで実装していくわけですが、使用する前にSDKの設定を忘れないようにしてください。

(同じプロジェクトでAWSを使用する箇所があるなら大抵設定済みかもです)

以下が参考になると思います。

では、早速なんですが...

先にコードをお見せしちゃいましょう!

 // clientの作成
  $s3Client = app()->make('aws')->createClient('s3');

  // コマンドを作成
  $command = $s3Client->getCommand('PutObject', array(
    'Bucket' => config('aws.s3')
    'Key'    => 'key',
  ));

  // 1分間有効な署名つきURLを発行
  $request = $s3Client->createPresignedRequest($command, '+1 minutes');

  return $request->getUri();
}

まず最初にLaravelのDIを用いてクライアントの作成を行っています。

もちろん、その場で作ってもいいと思います。

上記引用先で行っていますが、Laravelではautoloadは必要ありません(autoloadにあたる処理を裏でやってくれてるみたいです)。

なので、SDKの準備ができていればほとんど例の通りでクライアント作成もうまくいくはずです。

次に行っているのが、コマンドの作成です。

コマンド...?となるかもしれませんが、クライアントはそのS3バケットへのアクセスする権限を持っているコマンド実行者、そしてその操作の情報を書いたものをコマンドと理解してもらえれば大丈夫かと思います。

getCommandというAPIに対して、第一引数にコマンド名(ここではPutObjectですね)、そして第二引数にそのコマンド設定に必要なパラメータを配列として渡しています。

以下にてコマンドの詳細についてみることができます。

リンク先ではコマンドとして作っていませんね。

// コマンド生成
$command = $s3Client->getCommand('PutObject', [/* ... */]));
// リンク先の方法
$result = $client->putObject([/* ... */]);

が、一番上の方をみてみると...

Each of the following operations can be created from a client using $client->getCommand('CommandName'), where "CommandName" is the name of one of the following operations. Note: a command is a value that encapsulates an operation and the parameters used to create an HTTP request.

ざっくり訳すと...

以下に記載されている操作はクライアントを用いて $client->getCommand('CommandName') とすることで作成することができます。ただし、"CommandName"の箇所は以下に記載されている操作名を入れてください。コマンドは操作をカプセル化した値になっており、そのパラメータはHTTPリクエストを行うために扱われます。

つまり、操作をカプセルかしてHTTPリクエストで扱えるようにしているのがコマンドで、リンク先の例は直接その命令を実行しているわけです。

今回はPutObjectで、どのバケット(ルートとなるフォルダ的なもの)、どのキー(ルートフォルダ以下のどんなファイルかを示すパス的なもの)にそのオブジェクト(データ)をおけばいいの?という命令をここで実行するわけではなく、presignedURLとして実行したいわけなので、一旦カプセル化してあげる必要があるわけですね。

実際の例だと...

configの環境変数として設定しているバケット(examplebucketとすると...)に対してkeyというファイルとしてデータを保存する という一連の操作をカプセル化しています。

そのカプセル化したコマンドを使ってpresigneURLを発行すれば、ファイルがPUTされると同じ操作を行うPresignedURLを発行するこができるわけですね。

先ほどファイルやフォルダのお話をしましたが、厳密にはバケットの概念はフォルダとは似て非なるものらしいんですが...

S3の概形理解のため、ここでは上記で理解していただければいいかなと思います...

(僕自身違いについて理解できていないので勉強してきます...orz)

そんな感じで、どのバケットのどのキーにおけばいいのかと言うのを指定してpresignedURLで行う操作をコマンドとして作成します。

そして、その情報を用いてクライアントにcreatePresignedRequestを用いてコマンドをURLにするわけですね。

第二引数にはどれくらいの時間そのURLを有効にするかを指定できるようになっています。

あまりにも長すぎるとURLが漏れた場合にそのURLを知っている人が誰でもアクセスできてしまい、逆に短すぎるとそもそもそのURLを使う前に有効期限切れになってアクセスできなくなったりします。

サービスごとに適切な値を指定すると良さそうです。

そして、取得した情報の中からgetUri()で実際のpresignedURLを取得することができます。

あとは生成できたpresignedURLに対してPUTリクエストで保存したいファイルを送ってあげればS3へ反映されるはずです。

筆者はPostManを使用して行いましたが、PostManだと以下のように確認できました。

PUTにする。

Body選択後、binalyを選択

画像や動画を追加して、先ほど生成したpresignedURL入力してからSend。

最初ずっとform-dataからやろうとしてしまって全然できなかった...w

実行を追えたらS3の管理画面をみに行って、対象のbucket, keyに対してデータが追加されていればOKです!

エラーがあるとするなら大抵はcredentials(クライアント生成時にAWSへ権限が設定できていない)とか、region日本の方は大体東京リージョン `ap-northeast` 辺りな気がします。

メタデータを付与する

さて、先ほどPUTすることができましたが、今回実装した際、メタデータを付けてアップロードしたいなぁ...となり、それもやってみたので合わせて書いておきます。

方法は至って簡単でコマンド生成時に合わせて付与することができます。

例えばgroup_idとvideo_idという名前でメタデータを付与したい場合は以下のようにすれば可能です。

// コマンドを生成
$command = $s3Client->getCommand('PutObject', array(
    'Bucket' => config('aws.s3')
    'Key'    => 'key',
    'Metadata' => [
	       'group_id' => $groupId,
	       'video_id' => $videoId
    ]
));

簡単ですね!

ちなみにメタデータを付けてS3で確認すると...

このような感じで確認ができます。( `x-amz-meta-` は自動で付与されるようです。)

まとめ

さて、正直初めて知ったpresignedURLだったんですが...

最初知ったときはなんのことかさっぱりでした...w

なんでわざわざURL発行するの...?とか。

ただ、知ってみるとなるほどぉ!

そんなことができるのかぁ!って感じです!

ただ、ある程度はその動きを理解しておく必要があるかなぁとも思います。

presigneURLは有効期間中、URLを知っていれば誰でもS3へアクセスできてしまうわけなので、そこはしっかりと把握した上で使いたいですね。

それでわ!

おすすめの記事