Edited at

RMagick使ってlocalstackのS3に画像をアップしてブラウザで参照する

More than 1 year has passed since last update.

かなり時間かかっちゃった上に

有力な情報がなかなか得られず結構苦しんだのでメモ


local開発でS3が使いたいんだけど!

そもそものスタートはこちら。

AWS上で動かし知ているRoRのシステムで

諸事情によりレールに乗らずに開発をしていたのですが

ローカルの開発環境であるdocker上でS3のモックが欲しくなり調べて実装してみました。


それ「localstack」があるよ

githubはこちら

https://github.com/localstack/localstack

AWSのモックを提供してくれるとても優秀なプロジェクトです。

一つのインスタンスでサービス毎にポートを割り当てて稼働させているようです。

詳細はドキュメントで。


localstackを立ち上げる

今回はdocker-composeで立ち上げるので docker-compose.yml はこんな感じ


docker-compose.yml

version: '2'

services:
aws:
image: localstack/localstack
ports:
- 8080:8080
- 4567-4578:4567-4578
environment:
- DEFAULT_REGION=ap-northeast-1
- HOSTNAME=localstack.test
- DATA_DIR=/tmp/localstack
privileged: true

ports でポートを外部に開放してあげる事でブラウザからも参照できます。

環境変数に関しては

DEFAULT_REGION で東京に指定

HOSTNAME は内部でドメインに割り当てるのに必要

DATA_DIR はデータの永続化のために割り当てています。

たぶんこれであっているはず・・・

他にも大量に設定はあるので詳しくはgithubのREADMEでも読んでみてください。

設定が終わったら docker-compose up -d で立ち上げます


プロジェクトからアップをしてみる


環境設定とか

ローカルと本番で向き先を変えたいので設定ファイルと設定の読み込みを作ります


config/s3.yml

default: &default

id: <%= ENV.fetch("AWS_S3_ACCESS_KEY_ID") %>
access_key: <%= ENV.fetch("AWS_S3_SECRET_ACCESS_KEY") %>
region: ap-northeast-1
endpoint: https://s3.ap-northeast-1.amazonaws.com
bucket_name: oreno-bucket

development:
<<: *default
endpoint: http://localstack.test:4572

test:
<<: *default

staging:
<<: *default

production:
<<: *default
bucket_name: honban-no-bucket


こんな感じで設定ファイルを作って読み込みのスクリプトを置きます


config/initializers/s3.rb

Rails.application.config.s3 = YAML.load(ERB.new(File.read("#{Rails.root}/config/s3.yml")).result)[Rails.env]


環境情報は Rails.env に入っているのでここを参照

ymlの中でrubyのscriptを使いたいので ERB を使って読み込ませています。


アップロードの処理

コードで書くとこんな感じです


lib/s3/handler.rb

require 'aws-sdk-s3'

class S3::Handle
def initialize
@s3 = Aws::S3::Resource.new(
region: Rails.application.config.s3["region"],
endpoint: Rails.application.config.s3["endpoint"],
force_path_style: Rails.env.development?,
credentials: Aws::Credentials.new(
Rails.application.config.s3["id"],
Rails.application.config.s3["access_key"]
)
)
end

def upload_image(bucket_name, key_name, image)
begin
@s3.bucket(bucket_name)
# ここがファイル名とかファイルパスになる
.object(key_name)
# `acl` でアクセス制御を追加しておく
.put(body: image, acl: 'public-read')
return true
rescue => err
return false
end
end
end


あんまりrails的な書き方では無いんだろうなと思いつつ・・・

Aws::S3::Resource の引数として設定している中で

endpoint はドメインの向き先の指定を

force_path_style はbucket_nameがサブドメインに含ませるかどうかの設定になります。

localstackを使う際には少なくともこの2点が本番稼働の時と違う設定になるのでお気をつけください。


ブラウザで参照出来なくて困ったにゃん・・・

適当に放り投げて適当に名前付けて保存してブラウザでimgタグで表示しようとしたら出来なかったので

片っ端から jpeg に変換するという暴挙に出ました。


ImageMagickのインストール

yum で入れるなら1行で終わりです。

RUN yum -y install ImageMagick ImageMagick-devel

もしDebian系とかうぶんちゅとかであればググって下さい。

情報はたくさんあります。


RMagickのインストール

これもgemなんで一瞬ですね。

gemファイルに以下の行を追加して gem install でOKです。

gem "rmagick"


jpeg変換をかけてみる

formからPOSTされてきたファイルをjpegに変換するのでcontroller側はこんな感じです。


app/controller/images_controller.rb

class ImagesController

# ~~~~ 中略 ~~~~
# 画像のアップロードの処理
def upload_image
s3 = S3::Handle.new

# file名は固定なので今回はこんな感じ
file_name = 'suteki_na_image.jpg'

# pathの指定とか出来る
path = 'oreno/gazou/' + file_name

# fileの読み込みと変換
blob = s3.convert_image(params[:file].tempfile, file_name, 'JPEG')
if s3.upload_image(path, blob)
render json: {
# ドメインの設定とか読み込むためにメソッドを噛ましている
path: s3.make_image_url(path)
}
else
head 500
end
end
end


jpqgの実際の変換はこんな感じでやっています。


lib/s3/handler.rb

require 'RMagick'

class S3::Handle
# ~~~~ 中略 ~~~~
def convert_image(image, name, extension)
# `from_blob` じゃないとうまく読み込めなかったりとかするみたい
img = Magick::Image::from_blob(image.read).first

# ここで変換
img.format = extension

# ここで書き出し
blob = img.write(name).to_blob

# オブジェクトを破棄してメモリ開放
img.destroy!
return blob
end
end



ブラウザからアクセスしてみる

画像をアップロードしたらブラウザからアクセスしてみます。

pathは

http(s)://{domain}/{bucket_name}/{file_path}

の構成になるので、今回の場合は

http://localstack.test:4572/oreno-bucket/oreno/gazou/suteki_na_image.jpg

にアクセスすれば画像が見られるはずです。


「bucketが無い」ってエラーが出たんだけど!

aws-sdk-s3create_bucket メソッドがうまく動かずに苦労しましたが

通常運用で動的にbucketを作らないといけない事態は想定していなかったので

普通にaws-cliで作りました。

localstackのidとkeyは適当で大丈夫みたいです。

aws-cliについてはまた別の機会に・・・


まとめ

便利な道具があるのはありがたいけどドキュメントが疲れた(語彙力