S3互換のクラウドストレージは多くありますが、Laravel 10でOracleのS3互換ストレージを利用する記事が少なく、また参考にした記事の多くはLaravelのバージョンが古かったため、情報共有と備忘録を兼ねてこの記事を書きました
前提
Laravelプロジェクトを既に立ち上げているものとします
Oracle Cloud Infrastructure側の設定
Oracle Cloudは他クラウドに比べると情報が少ないので、クラウド操作部分も少し丁寧に書きます
エンドポイントはhttps://{バケット名前空間}.compat.objectstorage.{リージョン}.oraclecloud.com
になるため、まずはそれぞれの情報を取得します
リージョンの確認
ここから自分の利用リージョンに対応する文字列を探してください
例えば日本リージョンは大阪がap-osaka-1
、東京がap-tokyo-1
です
後でこの値はAWS_DEFAULT_REGION
として利用します
バケットの作成、バケット名前空間の確認
左上のハンバーガーメニューから、ストレージ>バケットを選択
コンパートメントを指定して、バケット名をつけて作成
作成したバケットの詳細のネームスペースがバケット名前空間になるので、覚えておきましょう
後でこの値はAWS_ENDPOINT
の一部として利用します
また、付けたバケット名はAWS_BUCKET
として後で利用します
権限を分けたアカウントを作成(Optional)
権限のスコープは必要最小にすべきです、必ず設定しましょう
ただし、テストなので管理者アカウントのままでも構わないという人は、#顧客認証キーの発行 まで読み飛ばしてください
また本筋と関係ないのでそこまで詳しく紹介しません
作成方法
グループを作成
アイデンティティとセキュリティ>ドメイン>グループ
仮の名前としてLaravel manager
として作成します
ユーザを追加
アイデンティティとセキュリティ>ドメイン>ユーザー
作成時、先に作ったグループに所属させます
ポリシを追加
アイデンティティとセキュリティ>ポリシー
ポリシーでは一行ごとに以下のような構文で権限を割り当てます
Allow group [group_name] to [verb] [resource-type] in compartment [compartment_name] where [condition]
バケットの読み取りとオブジェクト作成と取得の権限をLaravel manager
に与える例
Allow group 'Default'/'Laravel manager' to read buckets in tenancy
Allow group 'Default'/'Laravel manager' to manage objects in tenancy where any {request.permission='OBJECT_CREATE', request.permission='OBJECT_INSPECT'}
適当に作ったのでテナンシに対して有効になっちゃってます
自分に合ったポリシを作成してください
顧客認証キーの発行
ログインしているアカウントで発行する場合
右上のアカウント画像から、
ユーザープロファイル>顧客秘密キー
で秘密キーの生成を行います
作成した別のアカウントで発行する場合
ハンバーガーメニューから、
アイデンティティとセキュリティ>ドメイン>ユーザ>作成したアカウント>顧客秘密キー
で秘密キーの生成を行います
ドメインは多分Defaultになってるはず
顧客秘密キーの生成
アクセスキーシークレットの取得
以前はS3互換APIキーと呼ばれていたようです
適当な識別用の名前を付けて生成すると、アクセスキーのシークレットが表示されます
一度閉じると二度と表示されないので、必ずメモしましょう(画像のトークンは無効化済みです)
この値はAWS_SECRET_ACCESS_KEY
として利用します
アクセスキーの取得
その後顧客秘密キーの一覧に登録されているので、アクセスキーをコピーします
この値はAWS_ACCESS_KEY_ID
として利用します
Laravelの設定
S3パッケージをインストール
$ composer require league/flysystem-aws-s3-v3
$ composer install
ファイルの追加
追加・変更するファイル一覧
/
|- app
| |- Http
| | \- Controllers
| | \- UploadController.php
| \- Providers
| \- OciObjectStorageServiceProvider.php
|- config
| |- app.php
| \- filesystems.php
|- routes
| \- web.php
|- resources
| \- views
| \- upload.blade.php
\- .env
.env
コード内容
FILESYSTEM_DISK
をlocal
->oci
に変更
AWS_*
には上で設定した情報を追記
FILESYSTEM_DISK=oci
# ...
AWS_ACCESS_KEY_ID="<key-id>"
AWS_SECRET_ACCESS_KEY="<key-secret>"
AWS_DEFAULT_REGION="<cloud-region>"
AWS_BUCKET="<bucket-name>"
AWS_ENDPOINT="https://<bucket-namespace>.compat.objectstorage.${AWS_BUCKET}.oraclecloud.com"
AWS_USE_PATH_STYLE_ENDPOINT=true
app/Providers/OciObjectStorageServiceProvider.php
OCI登録するサービスプロバイダを作成
コード内容
<?php
namespace App\Providers;
use Aws\S3\S3Client;
use Illuminate\Support\ServiceProvider;
use Illuminate\Filesystem\AwsS3V3Adapter;
use Illuminate\Support\Arr;
use Storage;
use League\Flysystem\AwsS3V3\AwsS3V3Adapter as S3Adapter;
use \League\Flysystem\Filesystem as Flysystem;
class OciObjectStorageServiceProvider extends ServiceProvider
{
/**
* Register services.
*/
public function register(): void
{
//
}
/**
* Bootstrap services.
*/
public function boot()
{
if (config('filesystems.default') != 'oci') {
return;
}
Storage::extend('s3', function($app, $config) {
// illuminateのAwsS3V3AdapterでOCIのbucket名をパスのprefixに使うための設定
$config['directory_separator'] = '/';
$config['prefix'] = $config['prefix'] ?? null;
if($config['use_path_style_endpoint']) {
$config['prefix'] = $config['bucket']. $config['prefix'];
}
$client = new S3Client([
'credentials' => [
'key' => $config['key'],
'secret' => $config['secret'],
],
'region' => $config['region'],
'version' => '2006-03-01',
'bucket_endpoint' => true,
'use_path_style_endpoint' => true,
'endpoint' => $config['endpoint'],
]);
// S3クライアント
$s3_adapter = new S3Adapter($client, $config['bucket'], $config['prefix']);
// \Illuminate\Filesystem\FilesystemManager.php createflysystemのパクリ
$flysystem_adapter = new Flysystem($s3_adapter, Arr::only($config, [
'directory_visibility',
'disable_asserts',
'temporary_url',
'url',
'visibility',
]));
// IlluminateのAwsS3V3Adapter
$adapter = new AwsS3V3Adapter($flysystem_adapter, $s3_adapter, $config, $client);
return $adapter;
});
}
}
app\Http\Controllers\UploadController.php
コードはこちらのサイトを参考に一部を変更して使用しています
https://www.geekfeed.co.jp/geekblog/laravel_s3
コード内容
<?php
namespace App\Http\Controllers;
use Illuminate\Http\UploadedFile;
use Storage;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;
class UploadController extends Controller
{
//
public function index()
{
return view('upload');
}
public function store(Request $request){
// $request->file('file')->store(''); // storage/appに保存
// アップロードされたファイルを変数に格納
$upload_file = $request->file('file');
// ファイルがアップロードされた場合
if(!empty($upload_file)) {
// アップロード先S3フォルダ名
$dir = 'test';
// バケット内の指定フォルダへアップロード ※putFileはLaravel側でファイル名の一意のIDを自動的に生成してくれます。
$s3_upload = Storage::disk('s3')->putFile('/'.$dir, $upload_file);
// ※オプション(ファイルダウンロード、削除時に使用するS3でのファイル保存名、アップロード先のパスを取得します。)
// アップロードファイルurlを取得
$s3_url = Storage::disk('s3')->url($s3_upload);
// s3_urlからS3でのファイル保存名取得
$s3_upload_file_name = explode("/", $s3_url)[4];
// アップロード先パスを取得 ※ファイルダウンロード、削除で使用します。
$s3_path = $dir.'/'.$s3_upload_file_name;
return $s3_url;
}
}
}
config/app.php
作成したサービスプロバイダを登録
コード内容
<?php
return [
# ...
'providers' => ServiceProvider::defaultProviders()->merge([
# ...
App\Providers\OciObjectStorageServiceProvider::class,
# ...
])->toArray(),
# ...
config/filesystem.php
ここはデフォルトのままですが、一応
<?php
return [
# ...
'disks' => [
# ...
'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,
],
],
# ...
resources/views/upload.blade.php
アップロードテスト用のページ
コード内容
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Document</title>
</head>
<body>
<form method="POST" action="{{ route('upload') }}" enctype="multipart/form-data">
{{ csrf_field() }}
<input type="file" id="file" name="file" class="form-control" />
<button type="submit">アップロード</button>
</form>
</body>
</html>
routes/web.php
コード内容
# 追記
Route::resource('upload', App\Http\Controllers\UploadController::class)->name('index', 'upload');
これで設定は完了です
テスト
http://localhost/uploadにアクセスしてテストします
簡易アップロードformがあるので適当なファイルを送信します
送信に成功すると保存先のURLを表示します
バケットの詳細からアップロードされたのが確認出来たら成功です
参考