はじめに
Railsで構築された既存のWebアプリケーションに、ネイティブアプリから画像をPOSTできるようにしたかったので、テストプロジェクトを作成しました。今回は、Userモデルにimageというカラムを追加して、プロフィール画像(avatar)を保存するというイメージで作っています。
以下に手順を残しますが、少々雑なメモなので、参考程度にご覧いただければと思います。
また、 一部実装方法がわからなかったため、無理矢理やっている部分があります。以下に質問事項として記載しましたので、お分かりの方がいらっしゃればコメントなどいただけると幸いです。
質問内容(問題点)
iOS(Alamofire)から送信されるParametersに対して、どのようにStrongParametersを設定してよいかわからず、createの中で直接記述してしまっている。
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
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"}
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
そのほか修正
Rails.application.routes.draw do
root 'users#index'
resources :users
end
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
class User < ActiveRecord::Base
validates :name, presence: true
end
<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>
<h1>Users#show</h1>
<%= @user.name %>
<h1>Users#edit</h1>
<%= render 'form' %>
<h1>Users#new</h1>
<%= render 'form' %>
<%= 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カラムがない場合
mount_uploader :image, ImageUploader # 追記
$ rake db:migrate
private
def user_params
params.require(:user).permit(:name, :image) # 修正
end
アップローダの設定
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::RMagick # 確認
・
・
・
Viewの修正
<%= 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 %>
<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'
アップロードの処理を記述
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をパラメータ付きでポストする