Laravel 6.xでS3への保存・呼び出し方法

– hasManyの1M以下のファイルを、storage保存から、S3保存に切り替えたい
### 現状:storage保存

if($file = $request->file('file')){
            $path = $file->getClientOriginalName();
            $file->storeAs('./public/files/tmp/', $path);
            $inputs['path'] = $path;
        } else {
            $inputs['path'] = '';
        }

## 手順
### 1. AWSマネージメントコンソールログイン
https://ap-northeast-1.console.aws.amazon.com/
ユーザ名メニュー 「My Security Credentials」 -> IAMページ表示

### 2. S3用のIAM作成
Users -> Add user
1ページ目
– User name: [ ${project name} ]
– Select AWS access type: check [Programmatic access] ※Enables an access key ID and secret access key for the AWS API, CLI, SDK, and other development tools.

2ページ目
– Set permissions: [Attach existing policies directly]
— [AmazonS3FullAccess]

3ページ目
– Add tags (optional): none

4ページ目
– Review: nothing to do

5ページ目
– User name: ${project name}
– Access key ID : 新規発行
– Secret access key : 新規発行

### 3. S3 bucket作成
https://s3.console.aws.amazon.com/s3/home?region=ap-northeast-1
1ページ目
– Bucket name: [ ${project name} ]
– Region: [Asia Pacific (Tokyo)]

2ページ目
– Properties : 必要に応じて設定

3ページ目
– Block public access : off

4ページ目
– review -> create bucket

### 4. composerインストール
https://readouble.com/laravel/6.x/ja/filesystem.html#driver-prerequisites

// out of memoryとなるのでswapメモリを追加
$ sudo /bin/dd if=/dev/zero of=/var/swap.1 bs=1M count=1024
$ sudo /sbin/mkswap /var/swap.1
$ sudo /sbin/swapon /var/swap.1
$ free

// s3パッケージインストール
$ php composer.phar require league/flysystem-aws-s3-v3 ~1.0
// キャッシュアダプタインストール(公式ドキュメントに絶対に必要と書いてある。。)
$ php composer.phar require league/flysystem-cached-adapter ~1.0

### 5. envファイルにS3アクセス情報追記

AWS_ACCESS_KEY_ID=${access_key}
AWS_SECRET_ACCESS_KEY=${secrete_access_key}
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=${bucket name}

### 6. controllerから保存
putFileAs(‘S3ディレクトリ’, $file, ‘filename’, ‘public’) で保存。
ファイル名を気にしない場合はputFileでもOK

if($file = $request->file('file')){
            $path = $file->getClientOriginalName();
            Storage::disk('s3')->putFileAs('/', $file, $path,'public');
            $inputs['path'] = $path;
        } else {
            $inputs['path'] = '';
        }

### 7. S3ファイルの表示

Route::get('/read', function(){
	$path = Storage::disk('s3')->url('image.jpeg');
	return "<img src=".$path.">";
});

思ったより簡単でワロタ。ベタがきだと、もう少しコードが必要だった記憶があります。

Amazon S3のAWS署名バーション2の廃止

S3の署名バージョン2が廃止になる。

– 以前の AWS リージョンの一部では、Amazon S3 で署名バージョン 4 と署名バージョン 2 がサポート。ただし、署名バージョン 2 は廃止され、署名バージョン 2 の最終サポートは 2019 年 6 月 24 日に終了
https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/UsingAWSSDK.html#UsingAWSSDK-sig2-deprecation

2016年5月以前にリリースされた AWS SDK を使用する場合、次の表に示すように、署名バージョン 4 のリクエストが必要になることがある
AWS CLI

$ aws configure set default.s3.signature_version s3v4
$ aws configure set profile.your_profile_name.s3.signature_version s3v4

Java SDK

System.setProperty(SDKGlobalConfiguration.ENABLE_S3_SIGV4_SYSTEM_PROPERTY, "true");
-Dcom.amazonaws.services.s3.enableV4

JavaScript SDK

var s3 = new AWS.S3({signatureVersion: 'v4'});

PHP SDK

									
$s3 = new S3Client(['signature' => 'v4']);

Python-Boto SDK

[s3] use-sigv4 = True

Ruby SDK

s3 = AWS::S3::Client.new(:s3_signature_version => :v4)
s3 = Aws::S3::Client.new(signature_version: 'v4')

.NET SDK

AWSConfigsS3.UseSignatureVersion4 = true;

つまり、AWS SDKを使っている場合は、バージョンを確認
– AWS SDK for Java v1 →Java 1.11.x あるいは v2 in Q4 2018 にアップグレード。
– AWS SDK for Java v2 ※アップグレード不要
– AWS SDK for .NET v1 →3.1.10 以降にアップグレード
– AWS SDK for .NET v2 →3.1.10 以降にアップグレード
– AWS SDK for .NET v3 ※アップグレード不要
– AWS SDK for JavaScript v1 →主要バージョン V3 in Q3 2019 にアップグレード
– AWS SDK for JavaScript v2 →2.68.0 以降にアップグレード
– AWS SDK for JavaScript v3 →主要バージョン V3 in Q3 2019 にアップグレード
– AWS SDK for PHP v1 →主要バージョン V3 にアップグレード
– AWS SDK for PHP v2 →主要バージョン V3 にアップグレード
– AWS SDK for PHP v3 ※アップグレード不要
– Boto2 →Boto2 v2.49.0 にアップグレード
– Boto3 →1.5.71 (Botocore)、1.4.6 (Boto3) にアップグレード
– AWS CLI →1.11.108 にアップグレード
– AWS CLI v2 ※アップグレード不要
– AWS SDK for Ruby v1 →Ruby V3 にアップグレード
– AWS SDK for Ruby v2 →Ruby V3 にアップグレード
– AWS SDK for Ruby v3 ※アップグレード不要
– Go ※アップグレード不要
– C++ ※アップグレード不要

Upload the image from the php form to s3

It is necessary to make access right to s3 bucket beforehand with IAM.
Use php library to upload to s3. Here is code.

require 'vendor/autoload.php';

if(file_exists($_FILES['upfile']['tmp_name'])){
	$ext = substr($_FILES['upfile']['name'], strrpos($_FILES['upfile']['name'],'.') + 1);
	echo $ext."<br>";
	if(strtolower($ext) !== 'png' && strtolower($ext) !== 'jpg' && strtolower($ext) !== 'jpeg' && strtolower($ext) !== 'gif'){
		echo '画像以外のファイルが指定されています。画像ファイル(png/jpg/jpeg/gif)を指定して下さい';
		exit();
	}

	$tmpname = str_replace('/tmp/', '', $_FILES['upfile']['tmp_name']);
	echo $tmpname;
	$s3client = new Aws\S3\S3Client([
			'credentials' => [
					'key' => '',
					'secret' => ''
			],
			'region' => 'ap-northeast-1',
			'version' => 'latest',
	]);

	$result = $s3client->putObject([
			'Bucket' => 'zeus-image',
			'Key' => 'test.png',
			'SourceFile' => $_FILES['upfile']['tmp_name'],
			'Content-Type' => mime_content_type($_FILES['upfile']['tmp_name']),
	]);

}


?>
<div id="content">
<h2>画像管理</h2>
<hr>
<form action="#" method="POST" enctype="multipart/form-data">
<div id="drag-drop-area">
 <div class="drag-drop-inside">
  <p class="drag-drop-info">ここにファイルをアップロード</p>
  <p>または</p>
  <!-- <input type="file" value="ファイルを選択" name="image"> -->
  <p class="drag-drop-buttons"><input id="fileInput" type="file" value="ファイルを選択" name="upfile"></p>
      <input type="submit" value="送信">
   </div>
  </div>
</form>

HTML form view

s3

You can confirm that it is being uploaded properly.

S3にアップロードしてmysqlに登録:S3 / IAM編

まず、awsコンソールにログインし、S3でバケットを作成します。適当に[zeus-image]としておきましょう。リージョンはアジアパシフィック東京でいいでしょう。

続いてプロパティ
バージョニング、アクセスログの記録、タグ、オブジェクトレベルのログ記録、デフォルト暗号化、cloudWatchリクエストメトリクス、いずれも特に設定はしません。

続いてアクセス権
デフォルトではチェックボックスに全てチェックが入っています。


このバケットのパブリックアクセスコントロールリスト (ACL) を管理する
– 新規のパブリック ACL と、パブリックオブジェクトのアップロードをブロックする (推奨)
– パブリック ACL を通じて付与されたパブリックアクセスを削除する (推奨)

このバケットのパブリックバケットポリシーを管理する
– 新規のパブリックバケットポリシーをブロックする (推奨)
– バケットにパブリックポリシーがある場合、パブリックアクセスとクロスアカウントアクセスをブロックする (推奨)


このまま作成します。

IAMでユーザ追加
ユーザを追加していきます。

アクセスの種類で「プログラムによるアクセス」にチェックを入れます。

アクセスキー IDとシークレットアクセスキーを保存します。

続いて、対象ユーザからインラインポリシーの追加を押下します。

s3の指定bucketへのアクセス付与をします。

うおー、なんかやる気がでねーぞ。
続いてcomposerを入れていきます。

[vagrant@localhost app]$ curl -sS https://getcomposer.org/installer | php
All settings correct for using Composer
Downloading…

Composer (version 1.8.0) successfully installed to: /home/vagrant/local/app/composer.phar
Use it: php composer.phar

[vagrant@localhost app]$ php composer.phar install aws/aws-sdk-php
Invalid argument aws/aws-sdk-php. Use “composer require aws/aws-sdk-php” instead to add packages to your composer.json.

あれ??

S3にアップロードしてmysqlに登録

やりたいこと
– Laravelから画像をS3にアップロードしてmysqlにパスを登録

-> その為には?
– ブレークダウンして考える
– まず、s3に画像をアップロード
– その後、ユーザID、s3のバケットのパス、ファイル名、ファイルサイズ、ステータスをmysqlに登録する
– ユーザIDに紐づいた画像を画像管理の画面で表示させる

アップロードのHTML、JSまではできている。

<form action="#" method="POST" enctype="multipart/form-data">
        <div id="drag-drop-area">
         <div class="drag-drop-inside">
          <p class="drag-drop-info">ここにファイルをアップロード</p>
          <p>または</p>
          <!-- <input type="file" value="ファイルを選択" name="image"> -->
          <p class="drag-drop-buttons"><input id="fileInput" type="file" value="ファイルを選択" name="image"></p>
              <!-- <input type="submit" value="送信"> -->
           </div>
          </div>
            

      <div class="button_wrapper remodal-bg">
         <button type="submit" value="送信" id="square_btn" onClick="location.href='#modal'">登録</button>
      </div>
       </form> 

ログイン機能は後から作ろうかなと悠著に考えていたが、先に作った方が良いのかな。。
まずは、S3へのアップロードからやりましょう。

IAMロールを作成する

IAMロールを作成する
ポリシーには以下二つの権限をつける。
-AWSLambdaBasicExecutionRole
-AmazonS3FullAccess

'use strict';

const aws = require('aws-sdk');
const s3 = new aws.S3();

exports.handler = function(event, context){
	console.log('Received event:', JSON.stringify(event, null, 2));

	const uploadBucket = event.Records[0].s3.bucket.name;
	const key = event.Records[0].s3.object.key;

	const params = {
		Bucket: uploadBucket,
		Key: key
	};
	s3.getObject(params, function(err, data){
		if(err){
			console.log(err, err.stack);
			context.done(err, err.stack);
		} else {
			console.log('data: ', data);

			const copyBucket = 'sample-copy-bucket';
			const params = {
				Bucket: copyBucket,
				Key: key,
				Body: data.Body
			};
			s3.putObject(params, function(err, data){
				if(err){
					console.log(err, err.stack);
					context.done(err, err.stack);
				} else {
					console.log('data: ', data);
					context.suceed('complete!');
				}
			});
		}
	});
};

amazon lambdaとは?

amazon lambdaとは?
– 事前に定義したコードを実行するサービス
– 処理のトリガは何かしらのイベント(s3にファイル配置、ストリームデータ受信など)
– node.js//javaで実装
– サーバ、ロギング、スケーリングなどインフラ設計・管理が不要
– 実行回数によって課金

aws cloudTrail -> Source Bucket -> Amazon S3 -> AWS Lambda -> Lambda Function -> Access Policy -> Topic -> Amazon SNS

s3でbucketを作成します。

iamからロールを作成します。

s3を画像のcdnとして使おう

まずs3のバケットに画像をuploadして、パブリックのアクセス権を付与します。

できました。

取得した画像を別のドメインから接続します。

<img src="https://s3-ap-northeast-1.amazonaws.com/capital-technology/20181010114846.gif">

OK!

AWSには、ElastiCacheがあるようですね。
elastiCacheとは?
>ElastiCache は、クラウド内の分散型インメモリデータストア環境またはインメモリキャッシュ環境のセットアップ、管理、およびスケーリングが簡単になるウェブサービス
redisみたいなものか?

cloudfront

cloud front
-> 負荷を分散させ、大量のリクエストに対する対策を実施、通信にかかるレイテンシ(遅延)を改善

Amazon CloudFrontは、Amazon Web Services(AWS)のCDN(コンテンツデリバリネットワーク)サービス
– コンテンツファイルをサーバーから直接配信せず、CDNを介してユーザーに配信
– コンテンツをCloudFrontから配信すると、サーバーへのアクセスを減らせます。動的コンテンツをキャッシュすれば、データベースの負荷軽減も可能
– コンテンツがキャッシュされている限り、ユーザーは安定したレスポンスを得られる

localのcsvをmysqlにインポートする

1. まずdatabaseをつくります。
mysql> CREATE DATABASE click;
Query OK, 1 row affected (0.00 sec)

mysql> use click;

2. 続いて、tableを作ります。
mysql> create table articles(
-> id int primary key auto_increment,
-> login_id varchar(30),
-> role varchar(50),
-> name varchar(20),
-> password varchar(30),
-> mail varchar(255),
-> test_mail varchar(255),
-> updated_person varchar(50),
-> created_at datetime,
-> updated_at datetime
-> );

mysql> select * from articles;
Empty set (0.00 sec)

s3から取得したcsvをopenして1行目のカラムを飛ばしてmysqlにinsertしていきます。

try {
	$dbh = new PDO('mysql:host=localhost;dbname=click;charset=utf8','hoge','hogehoge', array(PDO::ATTR_EMULATE_PREPARES => false));
} catch(PDOException $e){
	exit('データベース接続失敗。'.$e->getMessage());
}

$stmt = $dbh->prepare('INSERT INTO articles VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,?)');

$dbh->beginTransaction();
$fp = fopen('article.csv', 'rb');
$i = 0;
while ($row = fgetcsv($fp)){
	if($i == 0){
		$i++;
		continue;
	}
	if ($row === array(null)){
		continue;
	}
	$executed = $stmt->execute($row);
	$i++;
}
fclose($fp);
$dbh->commit();

確認してみましょう。

mysql> select * from articles;
+—-+———-+——–+————–+———–+———————+————————–+—————-+———————+———————+
| id | login_id | role | name | password | mail | test_mail | updated_person | created_at | updated_at |
+—-+———-+——–+————–+———–+———————+————————–+—————-+———————+———————+
| 1 | user1 | master | taniguchi | passowrd | laravel@gmail.com | laravel_test@gmail.com | sasaki | 2018-09-21 21:39:07 | 2018-09-21 21:39:07 |
| 2 | user2 | master | goto | himitsu | laravel@hotmail.com | laravel_test@hotmail.com | sasaki | 2018-09-22 08:31:54 | 2018-09-22 08:51:32 |
| 3 | user3 | master | 橋本芳樹 | password3 | laravel@gmail.com | laravel_test@gmail.com | こばやし | 2018-09-22 15:03:11 | 2018-09-22 15:03:11 |
| 4 | user4 | master | 後藤大輔 | password4 | laravel@gmail.com | laravel_test@gmail.com | こばやし | 2018-09-22 15:48:56 | 2018-09-22 15:48:56 |
| 9 | user2 | master | goto | himitsu | laravel@hotmail.com | laravel_test@hotmail.com | sasaki | 2018-09-22 17:41:59 | 2018-09-22 17:41:59 |
+—-+———-+——–+————–+———–+———————+————————–+—————-+———————+———————+
5 rows in set (0.00 sec)

おいおいおい、やべーことになってる。
とりあえず、laravel -> mysql(1) -> csv -> s3 upload -> s3 import -> csv -> mysql(2)の流れは出来た。やはり、laravelが時間かかったな。frameworkの習得は時間がかかる。s3はセキュリティ周りをもっと学習する必要がある。
next
-> SSL
-> githubからdeploy