Paperclipは6000スター超えの有名gemだが、そのgithubからリンクされてる paperclip-azure-storageは3スターしかついておらず、ほとんどメンテもされていないっぽい。Rails4.1.4, paperclip4.2.0でazureのストレージを利用したときにやったことをまとめる。
今回はpaperclip-azure-storageを利用せず、自分で同様のプログラムを実装した。paperclip-azure-storageではsecrets.ymlからazureのストレージに関するパスワードを読むことができず、自分のニーズに合わなかったため。
1. Gemfileにwas-storageを追加
gem 'waz-storage'
2. config/initializer/azure-storage.rbというファイルを追加
Railsあんまり詳しくないので、本当におくべき場所がここなのかはよくわからない。
ファイルの内容は以下のような感じ。おおむね https://github.com/gmontard/paperclip-azure-storage/blob/master/storage.rb からのコピペだが、本家であるhttps://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/storage/s3.rb を参考にしたほうが良さそうだと後から気づいた。
module Paperclip
module Storage
module AzureStorage
def self.extended base
begin
require 'waz-blobs'
rescue LoadError => e
e.message << " (You may need to install the waz-storage gem)"
raise e
end
base.instance_eval do
@azure_credentials = @options[:azure_credentials] # parse_credentials(@options[:azure_credentials])
@container = @options[:azure_container] || @azure_credentials[:azure_container]
@account_name = @azure_credentials[:account_name]
@azure_host_alias = @options[:azure_host_alias]
@url = ":azure_host_alias"
WAZ::Storage::Base.establish_connection!(
account_name: @azure_credentials[:account_name],
access_key: @azure_credentials[:access_key])
end
Paperclip.interpolates(:azure_path_url) do |attachment, style|
"https://#{attachment.account_name}.blob.core.windows.net/#{attachment.container_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
end
Paperclip.interpolates(:azure_domain_url) do |attachment, style|
"https://#{attachment.account_name}.blob.core.windows.net/#{attachment.container_name}/#{attachment.path(style).gsub(%r{^/}, "")}"
end
end
def custom_url(style = default_style, ssl = false)
ssl ? self.url(style).gsub('http', 'https') : self.url(style)
end
def account_name
@account_name
end
def container_name
@container
end
def azure_host_alias
@azure_host_alias
end
def parse_credentials creds
@azure_credentials
end
def exists?(style = default_style)
# Note: コピペ元ではWAZ::Blobs::Container.find(container_name)[path(style)] を利用しているが、これは毎回リクエストを発行してしまうのであまり賢くないのでは?と思う
# 一方original_filenameだけで判断すると実際にはファイルのアップロードが失敗しているのにtrueを返してしまう可能性がありそう
# この部分は要改善っぽい
!!original_filename
end
# Returns representation of the data of the file assigned to the given
# style, in the format most representative of the current storage.
def to_file style = default_style
return @queued_for_write[style] if @queued_for_write[style]
if exists?(style)
file = Tempfile.new(path(style))
file.write(WAZ::Blobs::Container.find(container_name)[path(style)].value)
file.rewind
file
end
return file
end
def flush_writes #:nodoc:
@queued_for_write.each do |style, file|
begin
log("saving to Azure #{path(style)}")
WAZ::Blobs::Container.find(container_name).store(path(style), file.read, instance_read(:content_type), {:x_ms_blob_cache_control=>"max-age=315360000, public"})
rescue
log("error saving to Azure #{path(style)}")
## If container doesn't exist we create it
if WAZ::Blobs::Container.find(container_name).blank?
WAZ::Blobs::Container.create(container_name).public_access = "blob"
log("retryng saving to Azure #{path(style)}")
retry
end
end
end
@queued_for_write = {}
end
def flush_deletes #:nodoc:
@queued_for_delete.each do |path|
begin
log("deleting #{path}")
WAZ::Blobs::Container.find(container_name)[path].destroy!
rescue
log("error deleting #{path}")
end
end
@queued_for_delete = []
end
end
end
end
3. application.rbに以下を追加して、paperclipのストレージとしてazureストレージを利用する
config.paperclip_defaults = {
storage: :azure_storage,
path: ":class/:id/:attachment/:style/:filename",
azure_credentials: {
account_name: Rails.application.secrets.azure_credentials['account_name'],
access_key: Rails.application.secrets.azure_credentials['access_key']
},
azure_container: "YOUR_STORAGE",
url: ":azure_domain_url",
}
4. secrets.ymlに以下を追加、azure blobサービスのアカウント名、アカウントキーを設定する
azure_credentials:
account_name: your_account
access_key: "your accesskey"
これでひとまず、blobサービスへの画像のアップロード、取り出しは一通り動くようになった。ただし、アップロード失敗してblobサービスに画像がないときも, image.url がそれっぽいurlを返してくるので、その辺り治してくれる人をお待ちしている