Upload
zaru-sakuraba
View
1.316
Download
1
Embed Size (px)
Citation preview
GCP Storage と carrierwave
その先にあるのは
TGIF 2017-10-06 @zaru
@zaru
raysCI のファイルアップロードの歴史
初期
google-cloud-storage
GCP だしストレージは Storage 一択
公式の gem が google-cloud-storage
レポートファイルのアーカイブ目的だから
雑に公式 gem だけでやろう
require "google/cloud/storage"
storage = Google::Cloud::Storage.new
bucket = storage.bucket "my-bucket"
bucket.create_file "path/to/local.file.ext", "destination/path/file.ext"
Pros簡単に実装できて、シンプルで良い
ConsGCP への依存
モデルに関連付けるコードを書く必要あり
画像リサイズやURL生成は自前でやる必要あり
中期
carrierwave + fog-google
画像も表示したいし、アップロード処理も増えてきたから、ライブラリを使う
使い方は簡単。
class User < ActiveRecord::Base mount_uploader :avatar, AvatarUploaderend
user = User.find 1user.avatar#=> <AvatarUploader:0x0055883db34618 @model=#<User id: nil, name: nil, avatar: nil, created_at: nil, updated_at: nil>, @mounted_as=:avatar>
user.avatar.url#=> '/url/to/file.png'
fog-googlegem "fog-google"gem "google-api-client", "> 0.8.5", "< 0.9"gem "mime-types"
"> 0.8.5", "< 0.9"
嫌な予感
該当バージョン
最新バージョン
実際に導入してみると…
user = User.find 1user.avatar.read
Excon::Error::Socket: end of filereached (EOFError)
ファイルをダウンロードして読み込もうとすると死。
再現したりしなかったり、不安定。
Pros設定だけで実装がほぼ必要ない
画像リサイズ・URLの取得などができる
Cons古い google-api-client に依存している
不安定
よろしい、ならば独自Classだ
後期
独自Class + google-cloud-storage
carrierwave に戻る可能性もある
最低限の互換性を保ちたい
アプリが全体的にGCPに依存している
google-api-client は最新を使いたい
carrierwave 互換ポイント
class User < ActiveRecord::Base mount_uploader :avatar, AvatarUploaderend
mount_uploader でフィールドを指定できる
user = User.find 1user.avatar#=> <AvatarUploader>
対象フィールドを参照するとアップロードクラスが返ってくる
つまり .url や .read などのメソッドを生やせる
まずは、最低限ということでこの2つを満たすような形で実装を行う。
クラスメソッドとインスタンスメソッド
クラスメソッドを提供する module が必要
class Photo include Uploader mount_uploader :fooend
module Uploader def self.included base p "included by #{base}"
base.extend ClassMethods end
module ClassMethods def mount_uploader field p "setting #{field}" end endend
面倒なので ActiveSupport::Concern 使う
module Uploader extend ActiveSupport::Concern included do class << self def mount_uploader field p "setting #{field}" end end endend
AcriveRecord のフィールド参照メソッドをオーバーライド
user.avataruser.avatar =
この2つのメソッドを独自Classの処理に置き換えたい。
def mount_uploader field
set_name = "#{field}=".to_sym
class_eval <<-EOS def #{field} end
def #{set_name} file end EOSend
class_eval を使ってオーバーライド
独自Classを雑に用意して carrierwave の代表的なメソッドを作る
class BaseUploader def initialize model, field @model = model @field = field end def exists? end def read end def download local_path end
def path end
def url
独自Classインスタンスを参照するようにする
都度生成されないようにインスタンス変数に格納
class_eval <<-EOS
def #{field} @uploaders = {} unless defined? @uploaders @uploaders[__method__] ||= BaseUploader.new self, __method__end
EOS
ファイルアップロードの処理を独自Classに任せる
Railsだと ActionDispatch::Http::UploadedFile のはず
class_eval <<-EOS
def #{set_name} file return unless file.is_a? ActionDispatch::Http::UploadedFile uploader_name = __method__.to_s.gsub "=", "" send(uploader_name).set_file file.path, file.original_filenameend
EOS
今のままだと user.avatar = file とやったタイミングでアップロードされてしまう。とても嫌な気持ち
user.save した時にアップロードしたい
after_save を使う
included do after_save :file_upload_callback
def file_upload_callback # ファイルアップロードする処理 end
end
アップロードフィールドを知る必要がある
クラス変数に格納してみる
module Uploader extend ActiveSupport::Concern
included do class << self @@uploaders = [] end endend
class Photo include Uploader def self.uploaders @@uploaders endend
クラス変数?
module Uploader extend ActiveSupport::Concern
included do class << self @@uploaders = [] def mount_uploader field @@uploaders << field end end endend
class Photo include Uploader mount_uploader :foo mount_uploader :barend
Photo.class_variable_get(:@@uploaders)#=> [:foo, :bar]
取れてます
じゃあ、別のクラスを作ってみます
class Movie include Uploader mount_uploader :hoge mount_uploader :piyoend
それぞれ、呼び出します
Photo.class_variable_get(:@@uploaders)#=> [:hoge, :piyo]
Movie.class_variable_get(:@@uploaders)#=> [:hoge, :piyo]
上書きされてる
ActiveSupport の class_attribute を使う
module Uploader extend ActiveSupport::Concern
included do class_attribute :uploaders self.uploaders = [] class << self def mount_uploader field uploaders << field end end endend
Photo.uploaders#=> [:foo, :bar]
Movie.uploaders#=> [:hoge, :piyo]
めでたい
おわり