Edited at

paperclipのストレージとしてWindows Azureのblob storageを利用する

More than 3 years have passed since last update.

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 を参考にしたほうが良さそうだと後から気づいた。


config/initializer/azure-storage.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を返してくるので、その辺り治してくれる人をお待ちしている