pythonを利用したS3とEC2内一時ディレクトリのファイルやり取り

# はじめに
オンプレ環境のGPUを持っていないので、私はAWSでGPUインスタンスを立ててディープラーニングを行っている。
学習データを利用する際、EC2内に一時ディレクトリを作成して、そこにS3から学習データをダウンロードして使うことで、以下のような利点が得られるんじゃないかと思ったので、紹介する。

EC2インスタンス上にデータを入れっぱなしにしておくよりも、プログラムの実行が終わった際にEC2インスタンスからデータが消去される、かつS3に出力結果が上がるため、セキュリティの強化、出力結果紛失の危険性低減、EBS（ストレージ）の節約になる。

この記事では、EC2インスタンス内に一時ディレクトリを作成し、そこにS3からファイルをダウンロードする。
そして、一時ディレクトリからS3へのアップロードするまでを目指す。

# 必要作業
- EC2にIAMロールの設定
- pythonモジュールのインストール(python3を利用)
- コードの記述

# IAMロールの設定

## ロールの作成
①AWSマネジメントコンソールからIAMのページに移動
②左のメニュー一覧からロールを選択
③ロールを作成を選択

![_2020-07-17_17.28.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679107/af5895d9-3447-4126-1b70-54661411149a.png)


④ユースケースはEC2を選択
⑤次のステップへ

![_2020-07-17_17.35.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679107/bdd808ad-85a0-722f-3de6-d65ba20dfa7b.png)


⑥検索窓でS3と入力し検索
⑦AmazonS3FullAccessにチェックマークを入れる
⑧次のステップへ

![_2020-07-17_17.42.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679107/58403c6c-7bf1-a785-c7b7-e81e4dfe0982.png)


⑨タグは設定してもしなくてもどちらでも良いが、複数人でAWSを利用している場合は、管理のしやすさのため、Userのみ設定しておくと良い
⑩次のステップへ

![_2020-07-17_17.46.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679107/6b1aaf8f-e291-5e15-5303-cb70b7dc9d40.png)


⑪任意のロール名をつける（ロール名から何ができるロールなのかわかるようにしておくと楽）
⑫ロールの作成

![_2020-07-17_17.50.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679107/a5f13e27-a4ae-842e-f2e8-f4e2586cfd5b.png)


## EC2インスタンスへのIAMロールの割当

EC2インスタンスを作成する際に先程作成したIAMロールを割り当てる

※インスタンス作成時にしかロールの割当できないかも？作成後にロールが割り当てられるかは要検証

![_2020-07-17_18.04.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/679107/d009088f-0b73-d6a2-739b-dee3a048ab60.png)


# pythonモジュールのインストール
## 必要モジュール
AWSモジュール
- boto3
一時ディレクトリモジュール
- tempfile(python標準モジュールのためインストール不要)
## インストールコマンド
```jsx
pip install boto3
```

# コードの記述

以下のコードをノートブックファイルや.pyファイルにコピペすればダウンロード及びアップロードの完了

### ダウンロード元やアップロード先の設定

```jsx
import boto3
import tempfile

#ダウンロード元バケット名
bucket_name = 'hogehoge'
#アップロード先バケット名（今回はダウンロード先と同じ）
out_bucket_name = bucket_name

#バケットにある動画ファイル名
file_name = 'input.mp4'

#アップロード先のフォルダ名
#下記の例だとS3にresultフォルダが作成され、そこに出力される
out_folder_name = 'result'
```

### 一時ディレクトリの作成及びS3からのダウンロード

```jsx
#一時保存用ディレクトリの作成
tmpdir = tempfile.TemporaryDirectory()
tmp = tmpdir.name + '/'

#一時的な入力ファイル名（適当な名前で固定）
#固定された名前で一時フォルダにダウンロードされる
tmp_input = tmp + 'tmp.mp4'

#S3からファイルをダウンロード
s3_client = boto3.client('s3')
s3 = boto3.resource('s3')
bucket = s3.Bucket(bucket_name)
bucket.download_file(file_name, tmp_input)
```

### ダウンロードされたファイルの使用例

以下の例はopencvで読み込みをし、何も処理せず一時ディレクトリに別名保存している

(一時ディレクトリにtmp.mp4とoutput.mp4がある状態)

```jsx
import cv2

#一時ディレクトリに保存するビデオ名の設定
output_video =tmp + 'output.mp4'

#動画ファイルを読み込む
video = cv2.VideoCapture(tmp_input)

# 幅と高さを取得
width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
size = (width, height)

#総フレーム数を取得
frame_count = int(video.get(cv2.CAP_PROP_FRAME_COUNT))

#フレームレート(1フレームの時間単位はミリ秒)の取得
frame_rate = int(video.get(cv2.CAP_PROP_FPS))

# 保存用
fmt = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')
writer = cv2.VideoWriter(output_video, fmt, frame_rate, size)

for i in range(frame_count):
    ret, frame = video.read()
    ### ここに加工処理などを記述する ###
    writer.write(frame)

writer.release()
video.release()
cv2.destroyAllWindows()#ビデオの読み込み
```

### S3へのアップロード

以下の例は先に設定したS3アップロード先にoutput_video.mp4という名前で上で作成したビデオがアップロードされている。

```jsx
s3.Bucket(out_bucket_name).upload_file(output_video,out_folder_name+'/'+ 'output_video.mp4')
```

### 一時ディレクトリの削除

```jsx
tmpdir.cleanup()
```
# まとめ
それぞれの技術についてもっと細かく解説しているサイトはたくさんあると思うが、それをどうやってうまく組み合わせるのかが意外と難しいと思いこの記事を作成した。