LoginSignup
1
0

More than 3 years have passed since last update.

PHP Slim 環境構築(8) S3

Last updated at Posted at 2019-09-27

PHP Slim 環境構築(8) S3

Introduction

前回は、ローカルDynamoDBにアクセスしてみました。
今回は、ローカル用のS3互換サービスMinIOを使って、S3へのアクセスを行います。

MinIO

MinIOは、エンタープライズレベルのオブジェクトストレージサービスです。
・・・なんですが、手軽に使えるS3互換ストレージとして、ローカル開発環境によく使用されているようです。

変更点

ソースツリー

前回からの変更・追加ソースは以下の通りです。

$(PROJECTROOT)
  /compose
    docker-compose.yml
  /src
    /hoge
      /lib
        /Controller
          StorageController.php            (NEW!)
        /Model
      /public
        index.php
      /test
        RequestTest.php
    composer.json
    composer.lock

docker-compose.yml

新たにS3互換のminIO用コンテナを追加します。

なお、この記事を書いている途中でdocker-composeのlinksオプションはすでに時代遅れなのを知ってしまったので、今回からlinksは全て削除しています。すでにnetworksを使っているので、linksは冗長だったんですね。

  ..(略)..  
  storage:
    image: minio/minio
    volumes:
      - storage-data:/data
    container_name: storage
    ports:
      - "19000:9000"
    environment:
      MINIO_ACCESS_KEY: minio
      MINIO_SECRET_KEY: miniminio
    command: server /data
    networks:
      - local_net

index.php

DIにstorage用の設定とstorageオブジェクトの定義を行い、storage用のrouteを追加します。

なお、ローカルS3スタックを使用する際には、"use_path_style_endpoint"を有効にする必要があります。
このフラグが無効だと、S3ストレージへのアクセスポイントがサブドメインを使用した形式になるため、localhostを使った名前解決が面倒になるためです。

/src/hoge/public/index.php

..()..  

use Hoge\Controller\RedisController;
use Hoge\Controller\DynamodbController;
use Hoge\Controller\StorageController;                   (追加)

..()..  

$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
    'settings' => [

        ..()..  

        'storage' => [
            'endpoint' => 'http://storage:9000',
            'region' => 'ap-northeast-1'
        ]
    ],

    ..()..  

    'storage' => function (ContainerInterface $container) {
        $settings = $container->get('settings')['storage'];
        $sdk = new Aws\Sdk([
            'endpoint' => $settings['endpoint'],
            'region' => $settings['region'],
            'version' => '2006-03-01',
            'credentials' => [
                'key' => 'minio',
                'secret' => 'miniminio'
            ],
            //'bucket_endpoint' => true,
            'use_path_style_endpoint' => true
        ]);
        $s3 = $sdk->createS3();
        return $s3;
    }

..()..  

$app->group('/storage', function (RouteCollectorProxy $group) {
    $group->get('/{filename}', StorageController::class . ':get');
    $group->post('', StorageController::class . ':post');
});

$app->run();



### StorageController

ファイルのアップロードとファイルの表示(ダウンロードにするためにはcontent-typeとかいじればok)に対応しています

```php:/src/hoge/lib/Controller/StorageController.php
<?php

namespace Hoge\Controller;

use Aws\S3\Exception\S3Exception;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use Psr\Container\ContainerInterface;

class StorageController
{
    /**
     * @var ContainerInterface
     */
    protected $container;

    public function __construct(ContainerInterface $container)
    {
        $this->container = $container;

        /** @var \Aws\S3\S3Client $s3 */
        $s3 = $this->container->get('storage');
        try {
            // バケット確認
            $s3->headBucket(['Bucket' => 'dummy']);
        } catch (S3Exception $ex) {
            $errorCode = $ex->getAwsErrorCode();
            if ($errorCode === 'NotFound') {
                // バケットが無ければ作る
                $s3->createBucket([
                    'Bucket' => 'dummy',
                    'CreateBucketConfiguration' => [
                        'LocationConstraint' => 'ap-northeast-1'
                    ]
                ]);
            }
        }
    }

    public function get(Request $request, Response $response, array $args) : Response
    {
        // parameters (id)
        $key = $args['filename'];

        /** @var \Aws\S3\S3Client $s3 */
        $s3 = $this->container->get('storage');

        try {
            $result = $s3->getObject([
                'Bucket' => 'dummy',
                'Key' => $key
            ]);
        } catch (S3Exception $ex) {
            return $response->withStatus(404);
        }

        $body = $result->get('Body');
        $newResponse = $response->withBody($body);
        return $newResponse;
    }

    public function post(Request $request, Response $response, array $args) : Response
    {
        $files = $request->getUploadedFiles();
        /** @var \Psr\Http\Message\UploadedFileInterface $uploadedFile */
        $uploadedFile = $files['upload'];

        $err = $uploadedFile->getError();
        if ($err !== 0) {
            return $response->withStatus(500);
        }
        $clientFileName = $uploadedFile->getClientFilename();

        /** @var \Aws\S3\S3Client $s3 */
        $s3 = $this->container->get('storage');

        $result = $s3->putObject([
            'Bucket' => 'dummy',
            'Key' => $clientFileName,
            'Body' => $uploadedFile->getStream()
        ]);
        $url = $result->get('ObjectURL');

        $response->getBody()->write($url);
        return $response;
    }
}

composer.json & composer.lock

今までのcontrollerをテストするためのコードを追加したのに伴い、それに使用したPHPUnitをcomposerに追加しました。

$ composer require phpunit/phpunit -dev
/src/composer.json
{
    "require": {
        "slim/slim": "4.2.0",
        "slim/psr7": "0.5.0",
        "php-di/php-di": "6.0.9",
        "ext-pdo": "^7.2",
        "ext-json": "^1.6",
        "ext-redis": "^3.1",
        "ext-curl": "*",
        "aws/aws-sdk-php": "^3.112"
    },
    "require-dev": {
        "phpunit/phpunit": "^8.4"
    }
}

手動テスト

wgetでpost fileuploadをテストするために、以下のようなコマンドを実行しました。

postdata.txt
--cuthere
Content-Disposition: form-data; name="upload"; filename="videodrome.txt"
Content-Type: text/plain

THIS IS YOUR TAPE.
--cuthere--
$ wget http://hoge.localhost/.storage --header='Content-Type: multipart/form-data; boundary=cuthere' --post-file=postdata.txt

全部テスト

今までのControllerをテストするために、/src/hoge/test/RequestTest.phpを追加しました。
長いのでソースはここでは割愛します(リポジトリを直接参照ください)。

ここまでのソース

こちらでどうぞ。

更新

2019/10/09

  • use_path_style_endpointに関する説明を追加。
  • RequestTest.php投入に伴う修正
1
0
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
1
0