LoginSignup
19
15

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-11-10

はじめに

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

質問内容(問題点)

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

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

そのほか修正

routes.rb
Rails.application.routes.draw do
  root 'users#index'
  resources :users
end
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
user.rb
class User < ActiveRecord::Base
  validates :name, presence: true
end
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>
show.html.erb
<h1>Users#show</h1>
<%= @user.name %>
edit.html.erb
<h1>Users#edit</h1>
<%= render 'form' %>
new.html.erb
<h1>Users#new</h1>
<%= render 'form' %>
_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カラムがない場合
user.rb
mount_uploader :image, ImageUploader # 追記
$ rake db:migrate
users_controller.rb
 private
  def user_params
    params.require(:user).permit(:name, :image) # 修正
  end

アップローダの設定

image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  include CarrierWave::RMagick # 確認
  
  
  

Viewの修正

_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 %>
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インストール

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
AlamofireでMultipartFormDataをパラメータ付きでポストする

19
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
15