cakephp-file-storage の使い方メモ書き


CakePHP2.4.10

https://github.com/burzum/cakephp-file-storage こやつです。
大体は https://github.com/burzum/cakephp-file-storage/blob/master/Docs/Home.md に書いている通り。


https://github.com/knplabs/gaufrette の CakePHP対応させた形になっており、これを使っている限りはファイルの保存先を意識せずに LocalからAWS-S3やFtpなどに切り替えることができる。


## Install

Use Composer

```composer.json
{
    "config": {
        "vendor-dir": "app/Vendor/",
        "preferred-install": "source"
    },
    "require": {
        "burzum/FileStorage": "master",
        "knplabs/gaufrette": "*"
    },
    "extra": {
        "installer-paths": {
            "app/Plugin/FileStorage": ["burzum/FileStorage"],
        }
    }
}
```

もし、S3も使用するなら下記のような感じで組み合わせる形

```composer.json
{
    "config": {
        "vendor-dir": "app/Vendor/",
        "preferred-install": "source"
    },
    "require": {
        "burzum/FileStorage": "master",
        "knplabs/gaufrette": "*",
        "aws/aws-sdk-php": "2.6.*@dev"
    },
    "extra": {
        "installer-paths": {
            "app/Plugin/FileStorage": ["burzum/FileStorage"],
        }
    }
}
```

設定

```app/Config/bootstrap.php
<?php
/* 末尾の方に追記 */
App::uses('CakeEventManager', 'Event');
App::uses('FileStorageUtils', 'FileStorage.Lib/Utility');
App::uses('StorageManager', 'FileStorage.Lib');
App::uses('LocalImageProcessingListener', 'FileStorage.Event');
App::uses('LocalFileStorageListener', 'FileStorage.Event');
App::uses('FileStorageListener', 'FileStorage.Event');

// Only required if you are *NOT* using composer or another autoloader!
spl_autoload_register(__NAMESPACE__ .'\FileStorageUtils::gaufretteLoader');

// ローカルで使用する場合
$listener = new LocalFileStorageListener();
CakeEventManager::instance()->attach($listener);

// S3で仕様する場合
use Aws\S3;

App::uses('S3StorageListener', 'FileStorage.Event');
$listener = new S3StorageListener();
CakeEventManager::instance()->attach($listener);

// Optional, lets use the AwsS3 adapter here instead of local here
$S3Client = \Aws\S3\S3Client::factory(array(
    'key' => 'YOUR-KEY',
    'secret' => 'YOUR-SECRET'
));

// Configure the Gaufrette adapter through the StorageManager
StorageManager::config('S3Image', array(
    'adapterOptions' => array(
        $S3Client,
        'YOUR-BUCKET-NAME',
        array(),
        true
    ),
    'adapterClass' => '\Gaufrette\Adapter\AwsS3',
    'class' => '\Gaufrette\FileSystem')
);
```

## Setup

コマンドを打つ

```shell
> cake schema create --plugin FileStorage
```

CakeDC Migration を使っている場合、

```shell
> cake Migrations.migration run all --plugin FileStorage
```



## Model とかとか使う

ファイルを持たせたい`Model`に、`$hasOne`を持たせてリレーションを持たせる。複数持たせる場合は`hasMany`でやらせる。数を限定したく、それぞれのファイルに別々の意味を持たせたい場合は、`Model`にフィールドを持たせてやって云々。

```app/Controller/Product.php
<?php
class Product extends AppModel {
	// ... なんかいろいろある
	// 今回は Pdf と Thumbnail を持たせたい。
	public $hasOne = [
		'PdfFile'  => [
			"className"  => 'FileStorage.FileStorage',
			"foreignKey" => 'foreign_key',
			"conditions" => [  'PdfFile.id = Product.pdf_file' ]
		],
		'Thumbnail'  => [
			"className"  => 'FileStorage.FileStorage',
			"foreignKey" => 'foreign_key',
			"conditions" => [  'Thumbnail.id = Product. thumbnail_file' ]
		]
	];

	/* ちょっとしたおまじない。 */
    /**  */
    public function Path_Adapter_S3 ($Product) {
        return $Product['path'];
    }

    /**  */
    public function Path_Adapter_Local ($Product) {
        $filename = $this->PdfFile->stripUuid($Product["id"]);
        return $Product['path'] . $filename . '.' . $Product['extension'];
    }
}
```

アップロード処理。

```app/Controller/ProductController.php
class ProductController extends AppController {
	$uses = [ "Product" ];

	// 古いファイルは削除させるとか色々必要だけど面倒なので、何となくこんな感じという感じ
	public function upload () {
		if( $this->request->isPost() ){
			if( $this->Product->save($this->request->data) ){
				$key = $this->Product->getLastInsertId() . ".pdf";
				if( StorageManager::adapter('Local')->write($key, file_get_contents($this->request->data['PdfFile']['file']['tmp_name'])) ){
					$this->data['PdfFile']['foreign_key'] = $this->Product->getLastInsertId();
					$this->data['PdfFile']['model'] = 'Product';
					$this->data['PdfFile']['path'] = $key;
					$this->data['PdfFile']['adapter'] = 'Local';
					if( $this->Product->saveAll($this->data) ){
						$this->Session->setFlash(_("Success"));
					}
				}
			}
			return $this->redirect([ 'controller' => 'Product', 'action' => 'upload' ]);
		}
	}

	/** ダウンロード処理 */
	public function download ($id) {
		$file = $this->Product->findById($id);
		if( empty($file) ){
			throw new NotFoundException();
		}

		/* なんの仕様かわからんけど、LocalAdapterでreadした時の引数pathはファイルパスでないとダメなのに、
		   DBにはディレクトリパスが入っておりファイルまでたどりつかないので、無理やりファイルパスまで持たせる */
		$file["PdfFile"]['path'] = call_user_func_array(
            [$this->Product, "Path_Adapter_{$file["PdfFile"]['adapter']}"], [$file["PdfFile"]]);

		$adaptet = StorageManager::adapter($file["PdfFile"]['adapter']);
		if( $adaptet->has($file['PdfFile']['path']) === false ){
           $this->log("File is lost!!: ". print_r($file, true));
           throw new NotFoundException();
		}

		$this->response->autoRender = false;
		$this->response->body($adaptet->read($file["PdfFile"]['path']));
		$this->response->type($file["PdfFile"]["mime_type"]);
		$this->response->download($file['Product']['id'].".pdf");
		return $this->response;
	}
}
```