はじめに
AWS S3 を今まで使ったことがありませんでした。テキトーに使い始めてみたら、何やらよくわからないエラーで手間取ってしまいました。そこで、基礎の部分から調べてみようと思い、本投稿を残します。
特に、なぜか S3 フォルダを作成できない問題を解消したかったのでした。
まとめ
- S3 フォルダの作成するために
Storage::disk('s3')->makeDirectory($directory);
などと実行すると400 Bad Request
AccessControlListNotSupported
のエラーとなった。- これは Laravel 設定ファイル
laravel/config/filesystems.php
にて'visibility' => 'private',
を設定することで解消できた。 - 調査 → makeDirectory AccessControlListNotSupported – Google 検索
- これは Laravel 設定ファイル
遭遇したエラー達
AWS IAM ユーザー (アクセスキー IDとシークレットアクセスキーが紐づいているユーザーを指す) に権限が足りない場合
app@fe07f31e7473:/var/www/html/laravel$ php artisan tinker
Psy Shell v0.11.9 (PHP 8.2.0 — cli) by Justin Hileman
> $files = Storage::disk('s3')->allFiles('.');
League\Flysystem\UnableToListContents Unable to list contents for '.', deep listing
Reason: Error executing "ListObjectsV2" on "https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com?list-type=2&prefix="; AWS HTTP error: Client error: `GET https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com?list-type=2&prefix=` resulted in a `403 Forbidden` response:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>A69JFX (truncated...)
AccessDenied (client): Access Denied - <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>A69JFX4HC5PKRTZ6</RequestId><HostId>fcQ91/Aw7Ed3FO+3TnijfjMGaLD4gKdNRNdjhXS5sPjGCOMh7fLumx8pUe1eBhY2bjJbRKRnHlE=</HostId></Error>.
>
Laravel の設定が足りない場合
laravel/config/filesystems.php
で 'visibility' => 'private',
を追加しない場合に S3 フォルダを作成しようとした場合に発生したエラーです。
app@fe07f31e7473:/var/www/html/laravel$ php artisan tinker
Psy Shell v0.11.9 (PHP 8.2.0 — cli) by Justin Hileman
> Storage::disk('s3')->makeDirectory('dir');
League\Flysystem\UnableToWriteFile Unable to write file at location: dir/. Error executing "PutObject" on "https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com/dir/"; AWS HTTP error: Client error: `PUT https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com/dir/` resulted in a `400 Bad Request` response:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessControlListNotSupported</Code><Message>The bucket does not all (truncated...)
AccessControlListNotSupported (client): The bucket does not allow ACLs - <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessControlListNotSupported</Code><Message>The bucket does not allow ACLs</Message><RequestId>WMA9WF6B5G9D7740</RequestId><HostId>K+1NzKx5J/VFUQggExIvplHiguvK6S7napQ491sdXpkEHX1fd/O/RPoXX+3MlvsQVLc71PCw/tw=</HostId></Error>.
>
S3 バケットを作成する
- バケットを作成 – S3 bucket にて、次の内容で作成した。
- バケット名: myawsbucket-test-s3-1
- その他: デフォルトのまま
- オブジェクト所有者: ACL 無効 (推奨)
- このバケットのブロックパブリックアクセス設定: パブリックアクセスをすべてブロック
- バケット名: myawsbucket-test-s3-1
- その他: デフォルトのまま
作成した S3 バケットにアクセス可能な IAM ポリシーとそのポリシーを持つ IAM ユーザーを作成する
ポリシーの作成 – IAM Management Console から IAM ポリシーを作成した。
- エディタの JSON で次を入力した。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:*", "s3-object-lambda:*" ], "Resource": [ "arn:aws:s3:::myawsbucket-test-s3-1", "arn:aws:s3:::myawsbucket-test-s3-1/*" ] } ] }
- タグを追加では何もしなかった。
- ポリシーの確認で次を入力した。
- 名前: AmazonS3FullAccessToMyawsbucketTestS31
参考としたのは次の arn:aws:iam::aws:policy/AmazonS3FullAccess
です。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:*",
"s3-object-lambda:*"
],
"Resource": "*"
}
]
}
IAM ユーザーを、ポリシーを作紐付けて成した。 ユーザーを追加 – IAM Management Console のページより。
-
詳細
- ユーザー名: s3-test
- AWS 認証情報タイプを選択: アクセスキー – プログラムによるアクセス
-
アクセス権限
- 既存のポリシーを直接アタッチ
- AmazonS3FullAccessToMyawsbucketTestS31
- 既存のポリシーを直接アタッチ
-
タグでは何も行わなかった。
-
確認では何も行わず、ユーザーを作成した。
-
アクセスキー ID とシークレットアクセスキーをコピーし、控えた。
Laravel で設定する
S3ドライバ設定 – ファイルストレージ 9.x Laravel
composer require league/flysystem-aws-s3-v3 "^3.0"
laravel/.env
AWS_ACCESS_KEY_ID=IAM ユーザー作成時に得たアクセスキー ID
AWS_SECRET_ACCESS_KEY=IAM ユーザー作成時に得たシークレットアクセスキー
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=myawsbucket-test-s3-1
AWS_USE_PATH_STYLE_ENDPOINT=false
また、ディレクトリ作成でのエラーを解消するために、次の設定が必要となった。
laravel/config/filesystems.php
で 'visibility' => 'private',
を追加した。
's3' => [
'driver' => 's3',
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION'),
'bucket' => env('AWS_BUCKET'),
'url' => env('AWS_URL'),
'endpoint' => env('AWS_ENDPOINT'),
'use_path_style_endpoint' => env('AWS_USE_PATH_STYLE_ENDPOINT', false),
'throw' => false,
'visibility' => 'private',
],
Laravel tinker から S3 操作を試した時のコマンドまとめ
php artisan tinker
// 設定を確認
Storage::disk('s3')->getConfig();
// ファイルの CRUD
// ファイルの保存
$contents = '🗒ファイルの中身の文章です。✏️';
Storage::disk('s3')->put('file1.txt', $contents);
Storage::disk('s3')->put('dir1/file2.txt', $contents);
// ファイルの取得
$contents = Storage::disk('s3')->get('file1.txt');
$contents = Storage::disk('s3')->get('dir1/file2.txt');
// ファイルの前後への追加
Storage::disk('s3')->prepend('file1.txt', 'Prepended Text');
$contents = Storage::disk('s3')->get('file1.txt');
Storage::disk('s3')->append('file1.txt', 'Appended Text');
$contents = Storage::disk('s3')->get('file1.txt');
// ファイルのコピーと移動
Storage::disk('s3')->copy('file1.txt', 'file3.txt');
Storage::disk('s3')->copy('file1.txt', 'dir2/file4.txt');
Storage::disk('s3')->move('dir2/file4.txt', 'file5.txt');
// ↑ファイル移動により S3 フォルダにファイルが無くなり、 S3 フォルダも無くなった。
// 存在しないディレクトリへ、ディレクトリ作成しない状態での移動
Storage::disk('s3')->move('file5.txt', 'dir3/file6.txt');
// ファイルの削除
// 他ファイルのないディレクトリは AWS ウェブコンソールから存在が消えた。
Storage::disk('s3')->delete('dir3/file6.txt');
// ディレクトリの CRUD
// ディレクトリ内のすべてのファイルを取得
$files = Storage::disk('s3')->allFiles('.');
// ディレクトリを作成する
// `400 Bad Request` `AccessControlListNotSupported` のエラーとなった。これは設定が足りないためで、 Laravel 設定ファイル `laravel/config/filesystems.php` にて `'visibility' => 'private',` を設定することで解消できた。
$directory = 'dir4';
Storage::disk('s3')->makeDirectory($directory);
// ディレクトリを削除する
Storage::disk('s3')->deleteDirectory($directory);
// 存在しない S3 フォルダを削除すると true が返ったが、元々存在しないフォルダなため何も変化しない
$directory = 'aaaaaaaaaaaaaaa';
Storage::disk('s3')->deleteDirectory($directory);
// ファイルの存在する S3 フォルダを削除すると含まれるファイルも込みでフォルダごと削除された。
$directory = 'dir1';
Storage::disk('s3')->deleteDirectory($directory);
Laravel tinker から S3 操作を試した時の実践結果
app@fe07f31e7473:/var/www/html/laravel$ php artisan tinker
Psy Shell v0.11.9 (PHP 8.2.0 — cli) by Justin Hileman
> // 設定を確認
> Storage::disk('s3')->getConfig();
= [
"driver" => "s3",
"key" => "アクセスキー ID",
"secret" => "シークレットアクセスキー",
"region" => "ap-northeast-1",
"bucket" => "myawsbucket-test-s3-1",
"url" => null,
"endpoint" => null,
"use_path_style_endpoint" => false,
"throw" => false,
"visibility" => "private",
"version" => "latest",
"credentials" => [
"key" => "アクセスキー ID",
"secret" => "シークレットアクセスキー",
],
]
>
>
>
> // ファイルの CRUD
> // ファイルの保存
> $contents = '🗒ファイルの中身の文章です。✏️';
= "🗒ファイルの中身の文章です。✏️"
> Storage::disk('s3')->put('file1.txt', $contents);
= true
> Storage::disk('s3')->put('dir1/file2.txt', $contents);
= true
>
>
>
> // ファイルの取得
> $contents = Storage::disk('s3')->get('file1.txt');
= "🗒ファイルの中身の文章です。✏️"
> $contents = Storage::disk('s3')->get('dir1/file2.txt');
= "🗒ファイルの中身の文章です。✏️"
>
>
>
> // ファイルの前後への追加
> Storage::disk('s3')->prepend('file1.txt', 'Prepended Text');
= true
> $contents = Storage::disk('s3')->get('file1.txt');
= """
Prepended Text\n
🗒ファイルの中身の文章です。✏️
"""
> Storage::disk('s3')->append('file1.txt', 'Appended Text');
= true
> $contents = Storage::disk('s3')->get('file1.txt');
= """
Prepended Text\n
🗒ファイルの中身の文章です。✏️\n
Appended Text
"""
>
>
>
> // ファイルのコピーと移動
> Storage::disk('s3')->copy('file1.txt', 'file3.txt');
= true
> Storage::disk('s3')->copy('file1.txt', 'dir2/file4.txt');
= true
> Storage::disk('s3')->move('dir2/file4.txt', 'file5.txt');
= true
> // ↑ファイル移動により S3 フォルダにファイルが無くなり、 S3 フォルダも無くなった。
> Storage::disk('s3')->move('file5.txt', 'dir3/file6.txt');
= true
>
>
>
> // ファイルの削除
> // 他ファイルのないディレクトリは AWS ウェブコンソールから存在が消えた。
> Storage::disk('s3')->delete('dir3/file6.txt');
= true
>
>
>
>
>
>
> // ディレクトリの CRUD
> // ディレクトリ内のすべてのファイルを取得
> $files = Storage::disk('s3')->allFiles('.');
= [
"dir1/file2.txt",
"file1.txt",
"file3.txt",
]
>
>
>
> // ディレクトリを作成する
> // `400 Bad Request` `AccessControlListNotSupported` のエラーとなった。これは設定が足りないためで、 Laravel 設定ファイル `laravel/config/filesystems.php` にて `'visibility' => 'private',` を設定することで解消できた。
> $directory = 'dir4';
= "dir4"
> Storage::disk('s3')->makeDirectory($directory);
= true
>
>
>
> // ディレクトリを削除する
> Storage::disk('s3')->deleteDirectory($directory);
= true
> // 存在しない S3 フォルダを削除すると true が返ったが、元々存在しないフォルダなため何も変化しない
> $directory = 'aaaaaaaaaaaaaaa';
= "aaaaaaaaaaaaaaa"
> Storage::disk('s3')->deleteDirectory($directory);
= true
> // ファイルの存在する S3 フォルダを削除すると含まれるファイルも込みでフォルダごと削除された。
> $directory = 'dir1';
= "dir1"
> Storage::disk('s3')->deleteDirectory($directory);
= true
>
また、実践を通して残った全てのファイルを削除しました。
> $files = Storage::disk('s3')->allFiles('.');
= [
"file1.txt",
"file3.txt",
]
> Storage::disk('s3')->delete($files);
= true
>
INFO Ctrl+D.
app@fe07f31e7473:/var/www/html/laravel$
おわりに
今回知りたかったことは、調べてみても最初は 公式ドキュメント をコピペしたようなページしか見当たらなかったため、自らやってみた、という流れとなりました。
自分でやってみて、その上でさらに調べてみるとヒントや答えが見つかったりしました。面白いものだなと思います。
以上です。