# Amazon S3

S3のストレージは、各ファイルのコピーをAmazon S3に保存します。ローカルのファイルやデータベースのストレージと組み合わせて使えます。S3のストレージには、S3クライアントのプラグインが必要です。

<a id="installing-an-s3-client"></a>

## S3クライアントのインストール

Filebinderは次の2つのS3クライアントに対応しています。

- [pieceofcake2/async-aws](https://github.com/pieceofcake2/async-aws)経由の[AsyncAws](https://async-aws.com)
- [pieceofcake2/aws](https://github.com/pieceofcake2/aws)経由のAWS SDK for PHP

どちらか1つをインストールして読み込んでください。

<a id="asyncaws"></a>

### AsyncAws

pieceofcake2/async-awsとasync-aws/simple-s3をインストールします。

```bash
composer require pieceofcake2/async-aws async-aws/simple-s3
```

`config/bootstrap.php`でプラグインを読み込みます。

```php
CakePlugin::load('AsyncAws');
```

<a id="aws-sdk-for-php"></a>

### AWS SDK for PHP

pieceofcake2/awsをインストールします。

```bash
composer require pieceofcake2/aws
```

`config/bootstrap.php`でプラグインを読み込みます。

```php
CakePlugin::load('Aws');
```

<a id="choosing-the-client"></a>

## クライアントを選ぶ

Filebinderは、読み込まれているプラグインのクライアントを使います。両方のプラグインを読み込んでいる場合はAsyncAwsを使います。

クライアントを自分で選ぶには、`Filebinder.S3.client`に`'AsyncAws'`か`'Aws'`を設定します。

```php
Configure::write('Filebinder.S3.client', 'AsyncAws');
```

選んだクライアントのプラグインは、読み込んでおく必要があります。AWS SDK for PHPをインストールしただけでは使えません。使えるクライアントがないと、ファイルを保存するときに`FilebinderS3StorageException`が発生します。例外のメッセージには、インストールして読み込むべきパッケージが書かれています。

<a id="saving-files-on-s3"></a>

## S3にファイルを保存する

`storage`設定に`BindableBehavior::STORAGE_S3`を加え、フィールドにバケットを設定します。

```php
public $actsAs = [
    'Filebinder.Bindable' => [
        'storage' => [BindableBehavior::STORAGE_DB, BindableBehavior::STORAGE_S3],
    ],
];

public $bindFields = [
    [
        'field' => 'image',
        'filePath' => '/var/www/html/myapp/webroot/files/',
        'bucket' => 'my-bucket',
        'acl' => 'private',
    ],
];
```

ビヘイビアは、次のキーで各ファイルを保存します。

```text
<urlPrefix><model>/<model_id>/<field_name>/<file_name>
```

ローカルのファイルがないときは、`find()`がS3からダウンロードします。[ファイルの復元](https://pieceofcake2.github.io/filebinder/ja/storage.md#restoring-files)を参照してください。

S3に置くファイルの`Content-Type`は、ファイルの内容から決まります。Filebinderは、`file_content_type`と同じようにfileinfo拡張で判定します。ブラウザが送ったタイプやファイルの拡張子は使いません。

<a id="s3-settings"></a>

## S3の設定

フィールドごとに、`$bindFields`で`bucket`、`acl`、`urlPrefix`、`region`を設定できます。[ファイルのフィールド](https://pieceofcake2.github.io/filebinder/ja/configuration.md#bound-fields)を参照してください。これらを書いていないフィールドには、`Filebinder.S3`の設定を使います。

```php
Configure::write('Filebinder.S3', [
    'bucket' => 'my-bucket',
    'acl' => 'private',
    'urlPrefix' => 'files/',
]);
```

| 設定 | 説明 |
| --- | --- |
| `Filebinder.S3.client` | `'AsyncAws'`または`'Aws'`。デフォルトは読み込んだプラグインのクライアントです。 |
| `Filebinder.S3.bucket` | `bucket`のないフィールドで使うバケット。 |
| `Filebinder.S3.acl` | `acl`のないフィールドで使う既定ACL。デフォルトは`public-read`です。 |
| `Filebinder.S3.urlPrefix` | `urlPrefix`のないフィールドで使うキーの接頭辞。 |
| `Filebinder.S3.region` | 非推奨。代わりにプラグインの設定で`region`を指定してください。`region`のないフィールドで使うリージョンで、プラグインの設定より優先されます。 |
| `Filebinder.S3.key`、`Filebinder.S3.secret` | 非推奨。プラグインの設定で認証情報を指定するか、デフォルトの認証情報を使ってください。AWSのアクセスキーで、プラグインの設定より優先されます。 |

<a id="client-configuration"></a>

## クライアントの設定

Filebinderは、クライアントのプラグインの設定を使ってS3クライアントを作ります。リージョンと認証情報は、プラグインの設定に書いてください。

pieceofcake2/async-awsは`AsyncAws`の設定を読みます。`AsyncAws`がなければ`Aws`の設定を読みます。詳しくは[AsyncAwsの設定](https://pieceofcake2.github.io/async-aws/ja/#asyncaws-configuration)を参照してください。

```php
Configure::write('AsyncAws', [
    'region' => 'ap-northeast-1',
]);
```

pieceofcake2/awsは`Aws`の設定を読みます。詳しくは[AWSの設定](https://pieceofcake2.github.io/aws/ja/#aws-configuration)を参照してください。

```php
Configure::write('Aws', [
    'region' => 'ap-northeast-1',
]);
```

フィールドに`region`を書くと、そのフィールドのすべてのS3リクエストで、設定のリージョンの代わりにそのリージョンを使います。

<a id="credentials"></a>

### 認証情報

設定に認証情報がない場合、SDKはデフォルトの認証情報を使います。

- 環境変数
- 共有の認証情報ファイル
- EC2インスタンス、ECSタスク、Lambda関数のIAMロール

`Filebinder.S3.key`と`Filebinder.S3.secret`も引き続き使え、プラグインの設定より優先されます。ただし非推奨です。認証情報はプラグインの設定に移すか、デフォルトの認証情報を使ってください。

<a id="public-and-private-files"></a>

## 公開と非公開のファイル

Labelヘルパーがファイルをどうリンクするかは、ファイルのACLで決まります。ACLはフィールドの`acl`で、なければ`Filebinder.S3.acl`です。デフォルトは`public-read`です。

- `public-read`や`public-read-write`のような公開のACLでは、ヘルパーはオブジェクトのURLを出力します。このURLは誰でも開けます。
- `private`などそれ以外のACLでは、ヘルパーは署名付きURLを出力します。署名付きURLは`Filebinder.expire`の時間で期限が切れます。デフォルトは1分です。

ファイルを非公開にするには、ACLを`private`にします。

```php
Configure::write('Filebinder.S3.acl', 'private');
```

署名付きURLの署名はS3が確認します。ファイルのデータはアプリケーションを通りません。[ファイルのURL](https://pieceofcake2.github.io/filebinder/ja/label.md#urls-of-the-files)を参照してください。

<a id="s3-storage-api"></a>

## S3ストレージのAPI

`s3Storage()`は、フィールドのS3ストレージを返します。返り値は`FilebinderS3StorageInterface`のインスタンスで、ビヘイビアがそのフィールドに使うのと同じ設定になっています。

```php
$storage = $this->Post->s3Storage('image');
```

各メソッドの引数は、オブジェクトのバケットとキーです。

```php
$this->set('imageUrl', $storage->url('my-bucket', 'files/Post/1/image/photo.jpg'));
```

ストレージには次のメソッドがあります。

| メソッド | 説明 |
| --- | --- |
| `put(string $bucket, string $key, string $file, array $options = []): void` | ローカルのファイルをアップロードします。オプションには`ACL`を指定できます。 |
| `get(string $bucket, string $key): string` | オブジェクトの内容を返します。 |
| `saveAs(string $bucket, string $key, string $file): void` | オブジェクトをローカルのファイルにダウンロードします。 |
| `delete(string $bucket, string $key): void` | オブジェクトを削除します。 |
| `url(string $bucket, string $key): string` | クライアントが組み立てたオブジェクトのURLを返します。このURLには署名がありません。 |
| `presignedUrl(string $bucket, string $key, DateTimeImmutable $expires): string` | オブジェクトをダウンロードする署名付きURLを返します。URLは`$expires`で期限が切れます。 |

<a id="storage-factory"></a>

### ストレージのファクトリー

ストレージは`FilebinderS3StorageFactory`が作ります。先にクラスを読み込んでください。

```php
App::uses('FilebinderS3StorageFactory', 'Filebinder.Storage');
```

`FilebinderS3StorageFactory::create()`は、設定したクライアントのストレージを作ります。`region`や`credentials`などのクライアントのオプションを渡せます。

```php
$storage = FilebinderS3StorageFactory::create(['region' => 'ap-northeast-1']);
```

`FilebinderS3StorageFactory::client()`は、そのクライアントの名前として`'AsyncAws'`か`'Aws'`を返します。

```php
$client = FilebinderS3StorageFactory::client();
```
