laravel ローカル開発環境でS3互換のMiniOのバケットにputFileするとfalseが返る

## 概要

- `app/public/test_images/image.png`に存在するファイルをMiniOのpublicバケットにputFileしようとしてもfalseが返るので自分の場合の原因を突き止めてみた。

## 前提

- ローカル開発環境はDocker
- S3のファイルをいじるときに使うライブラリ（`league/flysystem-aws-s3-v3`）はインストール済み。composer.jsonにも下記のように記載されていることを確認済み。

    ```composer.json
    "league/flysystem-aws-s3-v3": "^3.16"
    ```

## 問題解決までの道のり

- laravelのコードが有るコンテナに入りtinkerを起動する。
- 下記を実行してアプリケーション → MiniOバケットに疎通確認をしてみる。

    ```php
    $localImagePath = storage_path('app/public/test_images/image.png');
    $file = new \Illuminate\Http\File($localImagePath);

    $pathInStorage = Storage::disk('s3')->putFile('test_folder', $file);
    ```

- やはりputFileでfalseが返る。上記のコードの出力内容をそれぞれ合わせてみてみる。
    - `$localImagePath = storage_path('app/public/test_images/image.png');`の結果（問題なさそう）

        ```
        = "/app/storage/app/public/test_images/image.png"
        ```

    - `$file = new \Illuminate\Http\File($localImagePath);`の結果（問題なさそう）

        ```
        = Illuminate\Http\File {#3869
            path: "/app/storage/app/public/test_images",
            filename: "image.png",
            basename: "image.png",
            pathname: "/app/storage/app/public/test_images/image.png",
            extension: "png",
            realPath: "/app/storage/app/public/test_images/image.png",
            aTime: 2023-10-26 23:31:20,
            mTime: 2023-08-01 17:59:33,
            cTime: 2023-10-26 14:54:41,
            inode: 7545195,
            size: 104934,
            perms: 0100644,
            owner: 0,
            group: 0,
            type: "file",
            writable: true,
            readable: true,
            executable: true,
            file: true,
            dir: false,
            link: false,
        linkTarget: "/app/storage/app/public/test_images/image.png",    }
        ```

    - `$pathInStorage = Storage::disk('s3')->putFile('test_folder', $file);`の結果（問題有りそう）

        ```
        = false
        ```

- `$file = new \Illuminate\Http\File($localImagePath);`の結果が問題なさそうと判断した理由は、`$localImagePath`を架空のものにしてから実行したらエラーになったため。

    ```
    $localImagePath = '/app/storage/app/public/test_images/foobar.png'
    = "/app/storage/app/public/test_images/foobar.png"

    $file = new \Illuminate\Http\File($localImagePath);

   Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException  The file "storagepublic/test_images/foobar.png" does not exist.
    ```

- こうなるとMiniOに疎通できていない可能性が非常に高い
- MiniOへの接続情報が間違えているかもしれない。取り急ぎlaravelの.envを確認してみる。

    ```.env
    AWS_USE_PATH_STYLE_ENDPOINT=true

    AWS_URL=http://locaohost:9000
    AWS_ENDPOINT=http://localhost:9000
    ```

- ぱっとみ問題なさそうに見えるが、PHPのコンテナとMiniOのコンテナは別コンテナである。そうなるとPHP→MiniOの通信はコンテナ間通信ということになる。
- Dockerのコンテナ間通信はdocker-composer.yamlのservicesで定義した名前を指定することでDocker側が勝手に内部DNSで解決してくれる。
- 筆者の環境はMiniOのServicesはminioという名前で定義しているので下記の様になる。（AWS_URLの方はオブジェクトへのパス生成で使うのでlocalhostのままにしておく、多分S3を使う環境の場合 `AWS_USE_PATH_STYLE_ENDPOINT=false`と`AWS_URL=`で良いはず。`AWS_USE_PATH_STYLE_ENDPOINT=true`とすると「`AWS_URL`を使ってストレージファサードでオブジェクトのURLを作成するよ」という設定になるはず。`AWS_URL=http://localhost:9000/バケット名`にバケット名を含めないとMiniOの場合完全なURLが生成できず、XMLのエラーが出ることがある。）

    ```.env
    AWS_USE_PATH_STYLE_ENDPOINT=true

    AWS_URL=http://localhost:9000/バケット名
    AWS_ENDPOINT=http://minio:9000
    ```

- これで今一度tinkerからputFileしてみる。

    ```php
    $localImagePath = storage_path('app/public/test_images/image.png');
    = "/app/storage/app/public/test_images/image.png"

    $file = new \Illuminate\Http\File($localImagePath);
    = Illuminate\Http\File {#3875
        path: "/app/storage/app/public/test_images",
        filename: "image.png",
        basename: "image.png",
        pathname: "/app/storage/app/public/test_images/image.png",
        extension: "png",
        realPath: "/app/storage/app/public/test_images/image.png",
        aTime: 2023-10-26 23:31:20,
        mTime: 2023-08-01 17:59:33,
        cTime: 2023-10-26 14:54:41,
        inode: 7545195,
        size: 104934,
        perms: 0100644,
        owner: 0,
        group: 0,
        type: "file",
        writable: true,
        readable: true,
        executable: true,
        file: true,
        dir: false,
        link: false,
      }

    $pathInStorage = Storage::disk('s3')->putFile('test_folder', $file);
    = "test_folder/kP3DDCldR5VPxpvlzj5TAqmOQZVWy7xWz7fSXWI4.png"
    ```

- アップロードできたっぽい。
- ブラウザで見ても設置できてた。良かった。解決した。

    ![MinIO_Console.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/936a813a-92b0-1e05-50de-f584bf094dff.png)

## おまけ　MiniO接続チェック項目

- [ ] ライブラリ（`league/flysystem-aws-s3-v3`）はインストール済み？！
- [ ] .envの下記の内容はそれぞれ記載されてる？
    - [ ] AWS_ACCESS_KEY_ID=docke-compose.ymlのenvironmentでMINIO_ROOT_USERに設定している値
    - [ ] AWS_SECRET_ACCESS_KEY=docke-compose.ymlのenvironmentでMINIO_ROOT_PASSWORDに設定している値
    - [ ] AWS_DEFAULT_REGION=us-east-1 → 固定
    - [ ] AWS_BUCKET=バケット名 → 手動か、docker-compose.ymlで自動作成したバケット名
    - [ ] AWS_USE_PATH_STYLE_ENDPOINT=true
    - [ ] AWS_URL=http://localhost:9000 → ポート名は設定によって異なる可能性があるよ
    - [ ] AWS_ENDPOINT=http://minio:9000 → docker-compose.ymlのMiniOコンテナのservicesの値だよ、localhostじゃないよ
- [ ] そもそもMiniOコンテナは起動している？
- [ ] そもそもバケットは存在している？