S3へフロントからPresigned URLを使って直接アップロードする記事は色々出ているので参考にすればサクッとできるかなと思っていたが権限周りでつまずいた
今回はその備忘録
想定ケース
- Presigned URLをRubyのサーバ側で生成
- フロント側でPresigned URLの発行リクエスト
- レスポンスの情報を使ってPresigned URLへファイルをアップロード
という流れ
ざっくり作ったサンプルコードで確認する
Presigned URL生成側(Ruby)
id = SecureRandom.hex bucket = Aws::S3::Resource.new.bucket(ENV['AWS_BUCKET']) key = "hoge/#{id}.#{request.params['extension']}" expires = Time.now presigned_object = bucket.presigned_post( key: key, success_action_status: '201', acl: 'public-read', content_type: extensions[request.params['extension'].to_sym], expires: expires ) { url: presigned_object.url, fields: presigned_object.fields }.to_json
コードは下記
s3-direct-upload-with-presigned-url/rack.ru at master · swfz/s3-direct-upload-with-presigned-url
フロント側のアップロード処理
<script type="text/javascript"> function upload() { const extension = document.querySelector("#image").value.split(".").slice(-1)[0]; console.log(extension); // presigned_urlの発行 fetch(`/presigned_post_url?extension=${extension}`) .then(res => res.json()) .then(json => { console.log(json) const file = document.querySelector("#image").files[0]; const formData = new FormData(); for (const key in json.fields) { formData.append(key, json.fields[key]) } formData.append('file', file); const headers = { 'accept': 'multipart/form-data' }; // アップロード fetch(json.url,{method: 'POST', headers, body: formData}).then((res) => { console.log('fetch'); console.log(res); }); }); } </script> <body> <input type="file" name="sample" accept="image/png,image/jpeg,image/gif,video/mp4" id="image" onchange="upload()"> </body>
コードは下記
s3-direct-upload-with-presigned-url/index.html at master · swfz/s3-direct-upload-with-presigned-url
こんなかんじで楽勝!と思ってたら全然うまくいかなかった
AccessDenied
この手のパターンがよくあるのかトラブルシューティング用のページが存在する
とりあえずはこの項目をチェックしていくのが近道なのではと思う
Amazon S3 から HTTP 403: Access Denied エラーをトラブルシューティングする
このページにも存在したがPresigned URLを発行するユーザーのIAMにs3:PutObjectAcl
が必要
今回はそれで結構時間を使ってしまった