どうも、ささおです!
今回はAPIKitで画像投稿してみようと思います!
取り回しが効くようにとにかくシンプルに書くので、カメラ機能だったり、アルバム機能と簡単に組み合わせられると思います。
一応続きものになっているので、僕が今まで書いたAPIKit関連の記事を見ていただければと思います。
APIKitの始め方を書いた記事
3,RailsのWebアプリとSwiftのiOSアプリを連携させてみた
Contents
Railsでの画像アップロード機能を作成する
今回は画像アップロード機能ライブラリは『Shrine』を使用して作成します。
他の有名なアップロード機能ライブラリと言えば『ImageMagick』などがありますね!
注:iOS側は画像アップロードライブラリがどちらでも変わらないので安心してください!
Shrineのインストール
gemfile内に
gem "shrine", github: 'janko-m/shrine'
を記述して、
$ bundle install
をしてください。
shrine.rbを作成する
shrineに関する初期設定を書いていきます。
app/config/initializers/下にshrine.rbを作成してください。
require "shrine" require "shrine/storage/file_system" # アップロードするディレクトリの指定 Shrine.storages = { cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"), } # プラグイン Shrine.plugin :activerecord
image_dataカラムの追加
前回まで作成していたNoteテーブルに画像を保存するカラムを作成します。
画像が入るカラムなので、imageという名前で作成したいです。
しかし、ここで注意が必要です。
shrineを使用して画像を保存する場合、カラム名には「_data」をつけてあげなければいけません。
なので今回は「image_data」カラムを追加します!
こちらをターミナルで打ってください。
$ rails g migration add_image_data_to_notes image_data:text
$ rails db:migrate
uploaderを作成
app/下にuploadersディレクトリを作成してください。
そして、app/uploaders/下にnote_image_uploader.rbを作成してください。
class NoteImageUploader < Shrine end
Shrineを継承させることが大事です!
modelを編集
app/models/Note.rbを編集していきます。
このように編集してください。
include NoteImageUploader[:image]
を追加しました。
NoteモデルとUploaderががっちゃんこしました!
class Note < ApplicationRecord include NoteImageUploader[:image] end
ストロングパラメータ
次はapp/controllers/notes_controller.rbを少しだけ編集します。
note_paramsにimageを追加することでストロングパラメータに対応しました。
注意が必要なのが、ここでは「image_data」ではなく「image」にすることです。
def note_params params.require(:note).permit(:content, :image) end
あとはformなどをいじればいい感じに画像投稿機能を実装できますが、画像投稿機能を実装するのはiOS側なので、今回は省きます。
iOS側に画像投稿機能を作成
まずはアップロードしたい画像を用意して、プロジェクトに入れておいてください。
基本的な流れ
1、Web側にデータを送るためにUIImageをDataに変換する。
その時に使用するのが、
UIImagePNGRepresentation(引数にUIImageを入れる)
これによって、PNGの拡張子がついた画像データを作成することができる。
2、Session.send()の引数にDataを入れる。
3、bodyParametersに入れてWeb側に送る
こんな感じになっています!
CreateViewController.swift
前回のコードを編集するとこのようになります。
import UIKit import APIKit class CreateViewController: UIViewController { @IBOutlet weak var imageView: UIImageView! @IBOutlet weak var titleTextField: UITextField! @IBOutlet weak var contentTextField: UITextField! // 画像データが入る var imageData: Data? override func viewDidLoad() { super.viewDidLoad() // aaa.pngは画像の名前です。 imageView.image = UIImage(named: "aaa.png") imageData = UIImagePNGRepresentation(imageView.image!) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } @IBAction func actionCreate(_ sender: Any) { Session.send(CreateNoteRequest(title: titleTextField.text, content: contentTextField.text, imageData: imageData!)) { result in switch result { case .success( _): print("作成完了!") case .failure(let err): print(err) } } } }
まずStoryboardにimageViewを配置して、IBOutlet接続をしました。(ここは省きます)
次にviewDidLoadないで配置したimageViewに、先ほど用意した画像を表示するように設定します。
その画像をUIImagePNGRepresentation()でpng形式の画像データに変換して、定義してある変数imageDataに格納します。
そのimageDataをSession.sendの引数に入れています。
Request.swift
struct CreateNoteRequest: WebRequest { var path: String { return "/notes" } typealias Response = Note var method: HTTPMethod { return .post } var title: String? var content: String? var imageData: Data? var bodyParameters: BodyParameters? { let titlePart = MultipartFormDataBodyParameters.Part(data: (title?.data(using: .utf8))!, name: "note[title]") let contentPart = MultipartFormDataBodyParameters.Part(data: (content?.data(using: .utf8))!, name: "note[content]") let imagePart = MultipartFormDataBodyParameters.Part( data: imageData!, name: "note[image]", mimeType: "image/png", fileName: "image.png") return MultipartFormDataBodyParameters(parts: [titlePart, contentPart, imagePart]) } func response(from object: Any, urlResponse: HTTPURLResponse) throws -> CreateNoteRequest.Response { return try decodeValue(object) } }
Request.swift内で変更した箇所はimageData関係です。
title, content. と同じように imageDataも変数として定義して、Session.sendからimageDataを初期化できるようにしました。
あとは、bodyParametersですね。
例によって、MultipartFormDataBodyParameters.PartでbodyParametersで使用できるようにしました。
他のtitleやcontentと違うところは、mimeTypeとfileNameですね。
mimeType
mimeTypeは送るファイルの形式です。
送るデータはpng形式なので、image/pngとしました。
fileName
これはそのまんまファイルの名前ですね。
適当でも問題なくアップロードできます。
これで画像投稿機能の作成完了です!
以外に大したことしないんですが(ライブラリが色々やってくれてる)、調べてもなかなか答えが出なかったのでメモしてみました!