joppot

コピペで絶対動く。説明を妥協しない

プログラミング

railsのaws-sdk gemを使ってs3に画像ファイルをフォームからアップロードする

投稿日:2014年6月13日 更新日:

Pocket

8g867

概要

みなさんこんにちはcandleです。比較的難しいrailsからs3に画像をアップロードする方法を試してみましょう。
一般にサーバ環境にはスケーラビリティ(アクセスの規模に応じて、動的にサーバを動かしたり止めたりする事)を持たせる為に、ファイル関連はEC2上に置くのではなく、s3というストレージサービスを使用します。もしも、個々のEC2上にファイルを置いてしまうと、複数EC2サーバがある場合他のEC2にアクセスすることができません。

概念図的には下の様になります。

aws

railsの場合、s3にファイルを上げるgemは2つあります。1つが最もポピュラーなaws-sdkです。これはs3に限らず様々なaws環境の処理をrailsから扱えるライブラリです。もう1つがs3特化したaws-s3というがあります。

当初私はaws-s3を使っていたのですが、試行錯誤した結果、aws-sdkを使っています。この記事もaws-sdkを使って説明していきます。

ちなみに、図ではEC2からアップロードしていますが、記事中で開発している場所は家のローカル環境のrailsです。

前提

railsの環境が構築されている
awsのアカウントを持っていて割と自由に使える

今回もtwitterbootstrap gemとscaffoldを使って説明します。別段、bootstrap gemが入っている必要がありませんが、見栄えが多少異なります。

rails4のtwitterbootstrap gem でscaffoldを作る方法は下のURLにあるので、もし、同じように進行したい場合は下の記事を参考にしてください。

https://joppot.s3.amazonaws.com/2014/05/27/1455

SPONSORED LINK


aws-gemを用意する

適当なプロジェクトを作成して、そのGemfileに下の内容を書き込みましょう。
私はbootstrap3というプロジェクトを使います。

gem 'aws-sdk'

gemaws

gemをインストールする為に下のコマンドを入力しましょう。

bundle install

恐らく、古いOSを使っているとnokogiri関連のgemが一緒にインストールされる際に、いろいろ表示されますが、ほっとけば勝手にインストールされます。

installawssdk

gemの準備が完了しました。


s3imageというscaffoldを作る

scaffoldを使って説明していきたいと思います。
scaffoldの詳しいことは別の記事に書いてあるので、ここではコマンドだけを紹介します。

下のコマンドでs3imageというscaffoldを作成します。

 rails g scaffold S3image title:string file_name:string comment:string

作成されたマイグレーションファイルをデータベースに反映させます。

bundle exec rake db:migrate

scaffoldが完成しました。

twitterbootstrapのテーマを反映させる

もしも、twitterbootstrap gemが入っているなら下のコマンドでテーマを反映させます。

rails generate bootstrap:install static

rails g bootstrap:layout application

rails g bootstrap:themed S3images


s3にファイルをアップするためのバケットを作る

railsのセットアップも終わったのでs3にバケットを用意しましょう。
awsのs3のサイトに飛んで、
indexs3

s3のサービスから「s3contents」というバケットをつくります。
createnewbucket

作成できました。

s3contentsbucket

s3contentsのバケットを選択してその中にimagesというフォルダを作ります。

imagesfolderins3

これでs3の準備はできました。


s3へアクセスるためのaccess_keyとsecret_keyを取得する

ここから、やや厄介になります。
s3へファイルアップロードしたり、消したりするのにはもちろん、権限が必要です。誰も彼もs3にファイルをアップロードできては危険だからです。

aws-sdk経由でs3にファイル上げる場合もaccess_keyとsecret_keyは必要不可欠です。それぞれのキーを取得しましょう。

awsのメニューの自分のアカウント名が書かれているプルダウンメニューから「Security Credentials」を選びます。

gosecurity

左のメニューから「Users」を選択します。

chooseUsers

「Create New Users」を選択します。

createnewiamuser

そしたら、名前は何でも良いのですが、例えば、「s3upload」というユーザー名にします。別にs3uploadという名前である必要性はありません。書いたら、Createを押します。

inputusername

access_keyとsecret_keyが表示されるので、コピペで保存するかダウンロードしましょう。ちなみにこれは後で確認ができなかったと思います。必ず保存しましょう。

saveaccesskey

今度はこのs3uploadユーザーに権限をもたせます。
ユーザーを選択して「Attach User Policy」を選択します。

attachuserpolicy

「Select Policy Template」から「Amazon S3 Full Access」を選択して「Select」を押します。

chooseuserpolicy

次の画面に移動するので、なにも変えずに「Apply Policy」を選択します。

okmakepolicy

これで、先ほど入手したaccess_keyとsecret_keyを使ってs3にファイルをアップロードできる権限を持つことができました。

s3imageのviewを変更する

では実際にコーディングに移って行きます。

railsサーバを起動して、下のパスに移動すると、
http://localhost:3000/s3images/new
図の様なフォームが現れます。

s3imageform

ここにはファイルをアップロードする項目がないのでそれを加えましょう。

下のパスのファイルを開きます。

app/views/s3images/_form.html.erb

下の記述はテキストフィールドになっているので、これをファイルがアップロードできる様に編集します。

<div class="form-group">
  <%= f.label :file_name, :class => 'control-label' %>
  <div class="controls">
    <%= f.text_field :file_name, :class => 'form-control' %>
  </div>
</div>

下の様にします。

<div class="form-group">
  <%= f.label :file, :class => 'control-label' %>
  <div class="controls">
    <%= f.file_field :file %>
  </div>
</div>

:file_nameの箇所を:fileに変更しているのは:file_nameは画像の名前をデータベースに入れる項目であり、フォームで書くわけではないからです。:fileは仮のハッシュ名です。もちろん:fileというのはテーブルのカラムには存在しません。この辺のややこしさはform_forになれれば解決します。

もう一度サイトを見るとファイルフィールドになっています。

goodfilefield


モデルを変更する

先ほど、フォームで:fileというs3imagesテーブルのカラムとは関係のないものを指定しました。form_forはテーブルのカラムと対応したフォームです。なので、テーブルには無いけれど、一応受け付ける為にモデルに下の記述を書きます。

attr_accessor :file

下の様な感じです。

atteracfile

モデルの設定は完了です。

コントローラーを編集する

app/controllers/s3images_controller.rbを開いて、
クラスの中に上部に下のセッティングソースを書きます。

AWS.config(access_key_id: 'あなたのアクセスキー', secret_access_key: 'あなたのシークレットー', region: 'ap-northeast-1')

ここで、先ほど入手した、アクセスキーとシークレットキーを入力しましょう。
したのような感じになると思います。

inputexample

regionを見ると、「ap-northeast-1」にしています。これはs3をどこに置いているかをさしています。それぞれ、確認してそれを適切に設定してください。
ちなみに、確認の仕方は、s3のサイトに言った時にURLに表示されます。

s3url

フォームからの値を許可する

s3imagesコントローラーが現在許可しているパラメーターは3つですが、

def s3image_params
  params.require(:s3image).permit(:title, :file_name, :comment)
end

上で編集したように:file_nameは:fileにしました。なので、:file_nameは削除して、代わりに:fileを受け入れるようにしましょう。

def s3image_params
  params.require(:s3image).permit(:title, :file, :comment)
end

createアクションを編集する

createアクションをしたのように編集します。

def create
  @s3image = S3image.new(s3image_params)
  s3 = AWS::S3.new
  bucket = s3.buckets['s3contents']
    
  file = s3image_params[:file]
  file_name = file.original_filename
  file_full_path="images/"+file_name
    
  object = bucket.objects[file_full_path]
  object.write(file ,:acl => :public_read)
  @s3image.file_name="http://s3-ap-northeast-1.amazonaws.com/s3contents/images/#{file_name}"
    
  respond_to do |format|
    if @s3image.save
      format.html { redirect_to @s3image, notice: 'S3image was successfully created.' }
      format.json { render action: 'show', status: :created, location: @s3image }
    else
      format.html { render action: 'new' }
      format.json { render json: @s3image.errors, status: :unprocessable_entity }
    end
  end
end

詳しく説明します

@s3image = S3image.new(s3image_params)
s3 = AWS::S3.new
bucket = s3.buckets['s3contents']

1行目はS3imgeの空のオブジェクトを作成しています。ここにフォームの値を収めたりして、データベースに保存します。

2行目はaws-sdkのAWSのS3クラスからs3インスタンスを作成しています。

3行目はs3インスタンスからbucketというオブジェクトを作成します。この時、バッケットの名前を指定することで、任意のバッケットを対象としたオブジェクトを作れます。
今回の場合ですと先ほど作成した「s3contents」というバッケットですね。

file = s3image_params[:file]
file_name = file.original_filename
file_full_path="images/"+file_name

S3imagesコントローラーの中のprivate関数の中にあるs3image_params関数を使ってフォームから送られてきた:fileの画像の全ての値をfile変数に収めます。

file.original_filenameを使ってファイル名だけをfile_nameに収めます。

三行目のfile_full_pathとはs3のs3contentsバケットから見て内部のどこにフィアルを置くのかのフルパスを記述します。例えば、s3contentsバケットのimagesフォルダの中にf.pngファイルを置く場合はfile_full_pathは”images/f.png”になります。

object = bucket.objects[file_full_path]
object.write(file ,:acl => :public_read)
@s3image.file_name="http://s3-ap-northeast-1.amazonaws.com/s3contents/images/#{file_name}"

bucketのobjects関数を使ってobjectというオブジェクトを作ります。
作成したobjectのwrite関数を使ってファイルをs3に保存します。この時、第二引数で:acl=>:public_readとする事で、第三者も見れる様になります。この引数がない場合は公開されません。

最後に@s3imageのfile_nameにs3にアップロードしたファイルのパスを収めます。
これで完成です。

実際にアップロードしてみる

下のURLに移動して

http://localhost:3000/s3images/new

必要事項を埋めて、

imageuploaddemo

Create S3imageを押すとファイルが無事アップロードされました。

successimageupload
S3を見てみると、
checks3

うまくいきましたね。

まとめ

手続き的な所が多いですね。とくにS3にアクセススためのアクセスキーやシークレットキーはs3のサービスではなくてIAMというサービスです。このようにawsのサービスをまたい使う場合は知らないとできないことが多いので、頑張ってください。

スポンサードリンク

「為になったなぁ」と思ったら、シェアお願いします。

-プログラミング
-

執筆者:


comment

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

関連記事

railsのrakeのmigrationファイルを削除しNO FILEとstatusに出た時の対処

概要 みなさんこんにちはcandleです。今回は誤ってデータベースに反映したマイグレーションファイルを不要だと思って削除した時に、NO FILEと出てきてしまった時の対処です。 言うなれば、下のような …

ruby on railsのckeditorの画像のアップロード先をcarrierwaveとfogを使ってS3にする

概要 railsのckeditorはデフォルトでrailsのuploadsフォルダにアップロードしますが、 それだとサーバの容量を取ってしまうので、画像のアップロード先をS3に上げてみたいと思います。 …

ruby on railsに最強のwysiwygエディタ、Squireを導入する

概要 みなさんこんにちはcandleです。今回はruby on railsにsquire editorを導入したいと思います。 squireはオープンソースの便利なwysiwygエディタです。 カスタ …

MAMPのSQLiteManagerを使用してsqlite3データベースの作成とデータベースファイルの設置

概要 みなさんこんにちはcandleです。今回はsqliteデータベースをMAMPを使用して作成しましょう。 PHP5.3からsqlite3のクラスが充実して、mysqlを使用しなくてもsqliteで …

phpのsnappyで複数のwebサイトのサムネイルを保存する

概要 前回、snappyの基本的な使い方を試しました。 今回はsnappyを使って、あらかじめ、定義してある複数のwebサイトのサムネイルを保存してみたいと思います。 前提 composerがインスト …

  • English
  • 日本語

プロフィール


ベンチャー企業のCTOをやってます。大学時代にプログラミングを始め、javaから入門し、C++へて、PHPへと進み、会社ではRailsを使用。自動化が大好きなプログラマー

スポンサードリンク

アーカイブ