Rails(4.2) + CarrierWaveにiOSからAlamofireを用いて画像をPOSTする


# はじめに
Railsで構築された既存のWebアプリケーションに、ネイティブアプリから画像をPOSTできるようにしたかったので、テストプロジェクトを作成しました。今回は、Userモデルにimageというカラムを追加して、プロフィール画像（avatar）を保存するというイメージで作っています。
以下に手順を残しますが、少々雑なメモなので、参考程度にご覧いただければと思います。
また、 **一部実装方法がわからなかったため**、無理矢理やっている部分があります。以下に質問事項として記載しましたので、お分かりの方がいらっしゃればコメントなどいただけると幸いです。

## 質問内容（問題点）
iOS(Alamofire)から送信されるParametersに対して、どのようにStrongParametersを設定してよいかわからず、createの中で直接記述してしまっている。

```ruby:users_controller.rb
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]
  ・
  ・
  ・
  def create
    # @user = User.new(user_params) # 本当はStrongParametersで設定したい
    @user = User.new(name: params[:name], image: params[:image])
    respond_to do |format|
      if @user.save!
        format.html {redirect_to(@user, notice: '更新に成功しました')}
      end
    end
  end
  ・
  ・
  ・
  private
    def user_params
      params.require(:user).permit(:name, :image) # ここの記述方法がわからない
    end

    def set_user
      @user = User.find(params[:id])
    end
end
```

```:iOSから送信されるデータ
Parameters: {"image"=>#<ActionDispatch::Http::UploadedFile:0x007fb8cbd0a260 @tempfile=#<Tempfile:/var/folders/c0/4lp6gymn56jbk4mm68qr2x8r0000gn/T/RackMultipart20151110-8521-961kkr.jpg>, @original_filename="test.jpg", @content_type="image/jpeg", @headers="Content-Disposition: form-data; name=\"image\"; filename=\"test.jpg\"\r\nContent-Type: image/jpeg\r\n">, "name"=>"test from ios"}
```


```:StrongParametersを設定した場合に発生するエラー
ActionController::ParameterMissing (param is missing or the value is empty: user):
  app/controllers/users_controller.rb:49:in `user_params'
  app/controllers/users_controller.rb:13:in `create'
```

以下は、備忘録を兼ねた手順です。

# Rails
## プロジェクト作成
```
$ mkdir sample_uploader
$ cd sample_uploader
$ bundle init
$ vi Gemfile
$ bundle install --path vendor/bundle
$ rails new .
$ mv app/assets/stylesheets/application.css app/assets/stylesheets/application.css.scss
```


## Gemfile修正
```
gem 'carrierwave' # 追記
gem 'rmagick' # 追記
```

## ユーザモデル作成
```
$ rails generate controller Users index new
$ rails generate model User name image
$ rake db:migrate
```

## そのほか修正
```ruby:routes.rb
Rails.application.routes.draw do
  root 'users#index'
  resources :users
end
```

```ruby:users_controller.rb
class UsersController < ApplicationController
  before_action :set_user, only: [:show, :edit, :update, :destroy]

  def index
    @users = User.all
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
      redirect_to users_path
    else
      render 'new'
    end
  end

  def show
  end

  def edit
  end

  def update
    if @user.update(user_params)
      redirect_to users_path
    else
      render 'edit'
    end
  end

  def destroy
    @user.destroy
    redirect_to users_path
  end

  private
    def user_params
      params[:user].permit(:name)
    end

    def set_user
      @user = User.find(params[:id])
    end
end
```

```ruby:user.rb
class User < ActiveRecord::Base
  validates :name, presence: true
end
```

```ruby:index.html.erb
<h1>Users#index</h1>
<% @users.each do |user| %>
  <p><%= link_to user.name, user_path(user.id) %>
    <%= link_to "[Edit]", edit_user_path(user.id) %>
    <%= link_to "[Destroy]", user_path(user.id), method: :delete, data: { confirm: "Are you sure?" } %>
      </p>
<% end %>

<p><%= link_to "Add New", new_user_path %></p>
```

```ruby:show.html.erb
<h1>Users#show</h1>
<%= @user.name %>
```

```ruby:edit.html.erb
<h1>Users#edit</h1>
<%= render 'form' %>
```

```ruby:new.html.erb
<h1>Users#new</h1>
<%= render 'form' %>
```

```ruby:_form.html.erb
<%= form_for @user do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>
  <% if @user.errors.any? %>
    <%= @user.errors.messages[:name][0] %>
  <% end %>
  <%= f.submit %>
<% end %>
```

## アップローダ作成
```
$ rails g uploader image
$ rails g migration add_image_to_users image:string # imageカラムがない場合
```

```ruby:user.rb
mount_uploader :image, ImageUploader # 追記
```

```
$ rake db:migrate
```

```ruby:users_controller.rb
 private
  def user_params
    params.require(:user).permit(:name, :image) # 修正
  end
```

## アップローダの設定
```ruby:image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick # 確認
  ・
  ・
  ・
``` 

## Viewの修正
```ruby:_form.html.erb
<%= form_for @user do |f| %>
  <%= f.label :name %>
  <%= f.text_field :name %>

  <%= f.label :image %>
　<%= f.file_field :image %>
　<%= image_tag @user.image.url if @user.image? %>

  <% if @user.errors.any? %>
    <%= @user.errors.messages[:name][0] %>
  <% end %>

  <%= f.submit %>
<% end %>
```

```ruby:show.html.erb
<h1>Users#show</h1>
<p><%= @user.name %></p>
<%= image_tag @user.image.url %>
```

## Controller修正
```
  def create
    # @user = User.new(user_params) # 本当はStrongParametersで設定したい
    @user = User.new(name: params[:name], image: params[:image])
    respond_to do |format|
      if @user.save!
        format.html {redirect_to(@user, notice: '更新に成功しました')}
      end
    end
  end
```

※ここで、いったんRailsから画像がアップロードできることを確認

# iOS
クライアント側は、CocoaPodsを利用してAlamofireをインストールし、multipartFormDataとして画像データを送信します。

## Alamofireインストール
```Podfile
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

pod 'Alamofire'
```

## アップロードの処理を記述
```ViewController.swift
import Alamofire
　・
　・
　・
let fileURL = NSBundle.mainBundle().URLForResource("test", withExtension: "jpg")!

Alamofire.upload(
    .POST,
    "http://localhost:3000/users",
    multipartFormData: { multipartFormData in
        multipartFormData.appendBodyPart(fileURL: fileURL, name: "image")
        if let data = "test from ios".dataUsingEncoding(NSUTF8StringEncoding) {
            multipartFormData.appendBodyPart(data: data, name: "name")
        }
        
    },
    encodingCompletion: { encodingResult in
        switch encodingResult {
        case .Success(let upload, _, _):
            upload.responseJSON { response in
                debugPrint(response)
            }
        case .Failure(let encodingError):
            print(encodingError)
        }
    }
)
```

以上です。


## 参考
[Alamofire/Alamofire · GitHub](https://github.com/Alamofire/Alamofire)
[AlamofireでMultipartFormDataをパラメータ付きでポストする](http://qiita.com/wmoai/items/4826817fe4f9bd10e880)