laravelで画像をs3アップロード


s3の設定
 ユーザー追加
 グループ追加
 バケットポリシー追加
 AWSのキー取得
laravelに処理追加
 AWSのキーをララベル.envに設定
 ※ここのキーがあってること、通るキーであることを初めに検証すると早い。
 ※他の細かい処理を書こうとも、そもそもキーとかでこけてると動かない。
 

インストール

たまにメモリエラー的なの出るときあるので、その時はメモリ使用量を増やす設定入れればよし。
v2とv3のライブラリがある。
v3の時にphp7.2以上使ってねってエラー出るかもしれないので、
その時はphpのバージョンあげる。

composer require league/flysystem-aws-s3-v3 ~1.0

何が一番デバッグ早いの?

tinkerでやるか、あるいは毎度laravelでserveする。
ユーザー認証のかかってない部分にルーティング1個追加して動かしたりする。
デバッガーとか使うのがより良いんだろうけど、時間ねぇので一旦これで行く。

S3のバケット作って初期設定する

バケットポリシーは真似しながら書く
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/example-bucket-policies.html
https://qiita.com/k-staging/items/f9d00fa0bca25db31888
https://aws.amazon.com/jp/premiumsupport/knowledge-center/s3-access-denied-listobjects-sync/

アクセスポイントにも注意
https://docs.aws.amazon.com/ja_jp/general/latest/gr/rande.html#s3_region

ルーティング

こんな感じの追加する。

routes/api.php
...
Route::post('/photo', 'HogeController@uploadImage');
...

コントローラに直書き

サービス分けたりとかやりたい人は後からしてください。

HogeController.php

    // ----------------------------------------
    // S3 アップロード
    // ----------------------------------------

    /**
     * @param Request $request
     * @return JsonResponse
     */
    public function uploadImage(
        Request $request
    ): JsonResponse {

        $this->validate($request, ['upload_image' => 'required|image']);
        $image = $request->file('upload_image');
        $filename = ((string)(uniqid("img_"))) .".". $image->getClientOriginalExtension();

        $filePath = Storage::disk('s3')
                        ->putFileAs(
                            'uploads'
                            , $image
                            , $filename
                            , [
                                'visibility' => 'public',
                                'ContentType' => 'image/jpeg'
                            ]
                        );
        return response()->json($filePath);
    }

実行

関係ありそうな場所のキャッシュをクリアすること

ルータ周り
php artisan route:clear
設定周り
php artisan config:cache
php artisan serve

postmanとかでpostする。

bodyでform-dataを選んで
keyをupload_imageにしてfileで画像を選択。

http://localhost:8000/api/photo

でpostmanを実行。

vue側も

panelIndexはここに書いてないけど、一応この部分は配列でクルクル回るようなコンポーネント。
1個のコンポーネントの時は削って問題なし。

<div class="large-12 medium-12 small-12 cell">
  <label>
    <input type="file" @change="onFileChange(panelIndex, $event)" />
  </label>
</div>

vueのdataにimgFileを定義。
ファイルアップロードされるとonFileChangeが走ってapiを叩きに行く。
laravelからurlが返却される。

...
     data() {
         return {
             imgFile: '',
         }
     },
...
 onFileChange(panelIndex, e){
   this.imgFile = e.target.files[0];

   this.submitFile(panelIndex);
 },
 submitFile(panelIndex) {
    let formData = new FormData();
    formData.append('upload_image', this.imgFile);

    axios.post( '/api/photo',
      formData,
      {
        headers: {
            'Content-Type': 'multipart/form-data'
        }
      })
      .then(response => response.data)
      .then(data => {
        this.$set(this.group.reply.panels[panelIndex], 'image_url', data.url)
      })
      .catch(function(){
        console.log('FAILURE!!');
      });
 },

その他参考

tinkerまとめ
http://kz-engineer-scrap.hatenablog.com/entry/2017/03/04/162158

https://qiita.com/D3vel0pper/items/45b2ddbfc68ee9cd7a3c
https://www.ritolab.com/entry/8
https://qiita.com/Ping/items/10ada8d069e13d729701

vueからaxiosで呼ぶ処理も書く時はここが良い。