2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【備忘録】初めてのCakePHP⑤ - ファイル操作編

Last updated at Posted at 2021-05-21

ディレクトリ操作

config/paths
define('ROOT', dirname(__DIR__)); // /var/www/cakapp
define('STORAGE_PATH', ROOT . '/src/storage');
src/Controllers/AppController.php
use Cake\Filesystem\Folder;
use Cake\Filesystem\File;

$dir = new Folder(STORAGE_PATH);
$dir->pwd(); // /var/www/cakeapp/src/storage
// '/var/www/cakapp/src/storage'ディレクトリ直下に'test'ディレクトリを作成
$dir->create('test'); // true
// ↑で作成した'test'ディレクトリの削除
$dir->cd('test'); // ディレクトリ移動
$dir->delete(); // true

ファイル操作

src/Controller/AppController.php
$f = new File(STORAGE_PATH. DS . 'tmp.png); // DSはDIRECTORY_SEPARATOR
$f->delete($tmpDir . DS . $deletePath);
$f->close();

画像のアップロード - 指定のディレクトリ

カラム追加

/var/www/cakeapp
$ bin/cake bake migration AddFileNameToUsers file_name:string
$ bin/cake migrations migrate

コントローラ

src/Controller/UsersController.php
<?php

namespace App\Controller;

use App\Controller\AppController;
use Cake\Collection\Collection;
use Cake\Event\Event;
use Cake\Filesystem\Folder;
use Cake\Filesystem\File;

class UsersController extends AppController
{

  // 中略

  public function edit()
  {
    $user = $this->Users->get($this->Auth->user('id'), [
      'contain' => [],
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
      $data = $this->request->getData();
      $fileSource = $data['user_file']['tmp_name'];
      //  $data['file_name']に"users/[$user->id]/thumbnail_[$user->id].[拡張子]"を格納する
      $saveDir = "users/{$this->Auth->user('id')}";
      $data['file_name'] = $this->getSavePath($data['user_file']['name']);
      // アップロードだけだと2回目以降はファイル名が被ってない限り、溜まる一方なので、削除用のパスを作っておく
      $deletePath = $this->Auth->user('file_name') !== null
        && $this->getExtension($this->Auth->user('file_name')) !== $this->getExtension((string)$data['file_name'])
        ? $this->Auth->user('file_name') : '';

      $user = $this->Users->patchEntity($user, $data);

      if ($this->Users->save($user)) {
        $this->Auth->setUser($user);

        $this->putTmpFile($fileSource, $saveDir, $data['file_name']);
        if (!empty($deletePath)) {
      $this->deleteTmpFile(STORAGE_PATH, $deletePath);        
        }
        $this->Flash->success(__('The user has been saved.'));
        return $this->redirect(['action' => 'edit']);
      }
      $this->Flash->error(__('The user could not be saved. Please, try again.'));
    }
    $this->set(compact('user'));
  }

  /**
   * @param string $originalName
   * @return string
   */
  protected function getSavePath(string $originalName): string
  {
    $fileName = "user_thumbnail_{$this->Auth->user('id')}";
    $extension = $this->getExtension($originalName);
    $fileName .= $extension;
    $saveDir = "users/{$this->Auth->user('id')}";
    return $saveDir . DS . $fileName;
  }

  protected function getExtension(string $filePath)
  {
    return mb_substr($filePath, mb_strrpos($filePath, '.'));
  }

  /**
   * @param $fileSource
   * @param $saveDir
   * @param $fileName
   */
  protected function putTmpFile($fileSource, $saveDir, $fileName)
  {
    $dir = new Folder(STORAGE_PATH);
    $dir->create($saveDir);
    $savePath = $dir->path . DS . $fileName;
    move_uploaded_file($fileSource, $savePath);
  }

  /**
   * @param string $tmpDir
   * @param string $deletePath
   */
  protected function deleteTmpFile(string $tmpDir, string $deletePath)
  {
    $f = new File($tmpDir . DS . $deletePath);
    $f->delete($tmpDir . DS . $deletePath);
    $f->close();
  }
}

ビュー

src/Template/Users/edit.ctp
<?php
echo $this->Form->create($user, ['enctype' => 'multipart/form-data']);
echo $this->Form->file('user_file', [
  'accept' => 'image/jpeg,image/png,image/gif,image/svg+xml'
]);
//echo $this->Form->control('body', ['rows' => '3']);
echo $this->Form->button(__('保存'));
echo $this->Form->end();
?>

画像のアップロード - S3

アップロードと削除だけ実装する。

aws/aws-sdk-phpのインストール

/var/www/cakeapp
$ composer require aws/aws-sdk-php

環境変数の設定

config/.env
export AWS_S3_BUCKET="asset.example.site"
export AWS_S3_REGION="ap-northeast-1"
export AWS_S3_KEY="AKIAHOGEHOGE"
export AWS_S3_SECRET="hoGeHoge"

コンポーネントの作成

/var/www/cakeapp
$ bin/cake bake component S3Client
src/COntroller/Component/S3ClientComponent.php
<?php

namespace App\Controller\Component;

use Cake\Controller\Component;
use Cake\Controller\ComponentRegistry;
use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;

/**
 * S3Client component
 */
class S3ClientComponent extends Component
{
  /**
   * Default configuration.
   *
   * @var array
   */
  protected $_defaultConfig = [];

  protected $defaultBucket;

  public function initialize(array $config)
  {
    $this->s3 = S3Client::factory([
      'credentials' => [
        'key' => env('AWS_S3_KEY', ''),
        'secret' => env('AWS_S3_SECRET', ''),
      ],
      'region' => env('AWS_S3_REGION', ''),
      'version' => 'latest',
    ]);

    $this->defaultBucket = env('AWS_S3_BUCKET', '');
  }

  /**
   * @param string $filePath
   * @param string $storePath
   * @param string $bucketName
   * @return mixed
   */
  public function putFile(string $filePath, string $storePath, string $bucketName=null)
  {
    try {
      if(!$bucketName) $bucketName = $this->defaultBucket;
      $result = $this->s3->putObject(array(
        'Bucket'       => $bucketName,
        'Key'          => $storePath,
        'SourceFile'   => $filePath,
      ));

      return $result;
    } catch (S3Exception $e) {
      echo $e->getMessage();
    }
  }

  /**
   * @param string $filePath
   * @param string $bucketName
   * @return mixed
   */
  public function deleteFile(string $filePath, string $bucketName=null)
  {
    try {
      if(!$bucketName) $bucketName = $this->defaultBucket;
      $result = $this->s3->deleteObject(array(
        'Bucket' => $bucketName,
        'Key'    => $filePath
      ));

      return $result;
    } catch (S3Exception $e) {
      echo $e->getMessage();
    }
  }

}

コントローラ

/var/www/cakeapp
$ bin/cake bake controller S3
src/Controller/S3Controller.php
<?php

namespace App\Controller;

use App\Controller\AppController;

/**
 * S3 Controller
 *
 *
 * @method \App\Model\Entity\S3[]|\Cake\Datasource\ResultSetInterface paginate($object = null, array $settings = [])
 */
class S3Controller extends AppController
{
  protected $storagePath;
  protected $cloudRootDir;

  public function initialize()
  {
    parent::initialize();
    $this->loadComponent('S3Client');
    $this->autoRender = false;
    $this->storagePath = STORAGE_PATH;
    $this->cloudRootDir = 'tmp/';
  }

  /**
   * @param $fileName (ex):'users/1/thumbnail_1.png'
   * @return mixed
   */
  public function upload(string $fileName)
  {
    $fileLocalPath = sprintf('%s/%s', $this->storagePath, $fileName);
    $fileStorePath = sprintf('%s%s', $this->cloudRootDir, $fileName);

    return $this->S3Client->putFile($fileLocalPath, $fileStorePath);
  }

  /**
   * @param string $deletePath = 'users/1/thumbnail_1.png'(ex)
   * @return mixed
   */
  public function delete(string $deletePath)
  {
    return $this->S3Client->deleteFile(sprintf('%s%s', $this->cloudRootDir, $deletePath));
  }
}

UsersControllerも修正する。

src/Controller/UsersController.php
  public function edit()
  {
    $user = $this->Users->get($this->Auth->user('id'), [
      'contain' => [],
    ]);
    if ($this->request->is(['patch', 'post', 'put'])) {
      $data = $this->request->getData();
      $fileSource = $data['user_file']['tmp_name'];
      //  $data['file_name']に"users/[$user->id]/thumbnail_[$user->id].[拡張子]"を格納する
      $saveDir = "users/{$this->Auth->user('id')}";
      $data['file_name'] = $this->getSavePath($data['user_file']['name']);
      // アップロードだけだと2回目以降はファイル名が被ってない限り、溜まる一方なので、削除用のパスを作っておく
      $deletePath = $this->Auth->user('file_name') !== null
        && $this->getExtension($this->Auth->user('file_name')) !== $this->getExtension((string)$data['file_name'])
        ? $this->Auth->user('file_name') : '';

      $user = $this->Users->patchEntity($user, $data);

      if ($this->Users->save($user)) {
        $this->Auth->setUser($user);

        $this->putTmpFile($fileSource, $saveDir, $data['file_name']);
+        $s3Controller = new S3Controller();
+        $s3Controller->upload($this->Auth->user('file_name'));
+        $this->deleteTmpFile(STORAGE_PATH, $this->Auth->user('file_name'));
        if (!empty($deletePath)) {
-      $this->deleteTmpFile(STORAGE_PATH, $deletePath);
+      $result = $s3Controller->delete($deletePath);
        }
        $this->Flash->success(__('The user has been saved.'));
        return $this->redirect(['action' => 'edit']);
      }
      $this->Flash->error(__('The user could not be saved. Please, try again.'));
    }
    $this->set(compact('user'));
  }
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?