Module: Paperclip::Storage::Azure
- Defined in:
- lib/paperclip/storage/azure.rb,
lib/paperclip/storage/azure/environment.rb
Overview
Azure's container file hosting service is a scalable, easy place to store files for distribution. You can find out more about it at http://azure.microsoft.com/en-us/services/storage/
To use Paperclip with Azure, include the azure-storage-blob gem in your Gemfile:
gem 'azure-storage-blob'
There are a few Azure-specific options for has_attached_file:
-
azure_credentials: Takes a path, a File, a Hash or a Proc. The path (or File) must point to a YAML file containing thestorage_access_keyandstorage_account_namethat azure gives you. You can 'environment-space' this just like you do to your database.yml file, so different environments can use different accounts:development: storage_account_name: foo storage_access_key: 123... test: storage_account_name: foo storage_access_key: abc... production: storage_account_name: foo storage_access_key: 456...This is not required, however, and the file may simply look like this:
storage_account_name: foo storage_access_key: 456...In which case, those access keys will be used in all environments. You can also put your container name in this file, instead of adding it to the code directly. This is useful when you want the same account but a different container for development versus production. When using a Proc it provides a single parameter which is the attachment itself. A method #instance is available on the attachment which will take you back to your code. eg. class User has_attached_file :download, :storage => :azure, :azure_credentials => Proc.new{|a| a.instance.azure_credentials }
def azure_credentials { :container => "xxx", :storage_account_name => "xxx", :storage_access_key => "xxx" } end end -
container: This is the name of the Azure container that will store your files. Remember that the container must be unique across the storage account. If the container does not exist Paperclip will attempt to create it. The container name will not be interpolated. You can define the container as a Proc if you want to determine it's name at runtime. Paperclip will call that Proc with attachment as the only argument. -
path: This is the key under the container in which the file will be stored. The URL will be constructed from the container and the path. This is what you will want to interpolate. Keys should be unique, like filenames, and despite the fact that Azure (strictly speaking) does not support directories, you can still use a / to separate parts of your file name. -
region: Depending on the region, different base urls are used. Supported values :global, :de
Defined Under Namespace
Classes: Environment
Class Method Summary collapse
Instance Method Summary collapse
- #auto_connect_duration ⇒ Object
- #azure_account_name ⇒ Object
- #azure_base_url ⇒ Object
- #azure_container ⇒ Object
- #azure_credentials ⇒ Object
- #azure_host_alias ⇒ Object
- #azure_interface ⇒ Object
- #azure_object(style_name = default_style) ⇒ Object
- #azure_prefixes_in_alias ⇒ Object
- #azure_protocol(style = default_style, with_colon = false) ⇒ Object
- #azure_uri(style_name = default_style) ⇒ Object
- #container_name ⇒ Object
- #copy_to_local_file(style, local_dest_path) ⇒ Object
- #create_container ⇒ Object
- #exists?(style = default_style) ⇒ Boolean
- #expiring_url(time = 3600, style_name = default_style) ⇒ Object
-
#flush_deletes ⇒ Object
:nodoc:.
-
#flush_writes ⇒ Object
:nodoc:.
- #obtain_azure_instance_for(options) ⇒ Object
- #parse_credentials(creds) ⇒ Object
- #save_blob(container_name, storage_path, file, write_options) ⇒ Object
Class Method Details
.extended(base) ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/paperclip/storage/azure.rb', line 61 def self.extended base begin require 'azure/storage/blob/blob_service' rescue LoadError => e e. << " (You may need to install the azure-storage-blob gem)" raise e end unless defined?(::Azure::Core) base.instance_eval do @azure_options = @options[:azure_options] || {} unless @options[:url].to_s.match(/\A:azure.*url\z/) || @options[:url] == ":asset_host".freeze @options[:path] = path_option.gsub(/:url/, @options[:url]).sub(/\A:rails_root\/public\/system/, "".freeze) @options[:url] = ":azure_path_url".freeze end @options[:url] = @options[:url].inspect if @options[:url].is_a?(Symbol) @http_proxy = @options[:http_proxy] || nil end unless Paperclip::Interpolations.respond_to? :azure_alias_url Paperclip.interpolates(:azure_alias_url) do |, style| protocol = .azure_protocol(style, true) host = .azure_host_alias path = .path(style). split("/")[.azure_prefixes_in_alias..-1]. join("/"). sub(%r{\A/}, "") "#{protocol}//#{host}/#{path}" end end Paperclip.interpolates(:azure_path_url) do |, style| .azure_uri(style) end unless Paperclip::Interpolations.respond_to? :azure_path_url Paperclip.interpolates(:asset_host) do |, style| "#{.path(style).sub(%r{\A/}, "".freeze)}" end unless Paperclip::Interpolations.respond_to? :asset_host end |
Instance Method Details
#auto_connect_duration ⇒ Object
134 135 136 137 |
# File 'lib/paperclip/storage/azure.rb', line 134 def auto_connect_duration @auto_connect_duration ||= @options[:auto_connect_duration] || azure_credentials[:auto_connect_duration] || 10 @auto_connect_duration end |
#azure_account_name ⇒ Object
143 144 145 146 147 148 |
# File 'lib/paperclip/storage/azure.rb', line 143 def azure_account_name account_name = @options[:azure_storage_account_name] || azure_credentials[:storage_account_name] account_name = account_name.call(self) if account_name.is_a?(Proc) account_name end |
#azure_base_url ⇒ Object
183 184 185 |
# File 'lib/paperclip/storage/azure.rb', line 183 def azure_base_url Environment.url_for azure_account_name, azure_credentials[:region] end |
#azure_container ⇒ Object
187 188 189 |
# File 'lib/paperclip/storage/azure.rb', line 187 def azure_container @azure_container ||= azure_interface.get_container_properties container_name end |
#azure_credentials ⇒ Object
139 140 141 |
# File 'lib/paperclip/storage/azure.rb', line 139 def azure_credentials @azure_credentials ||= parse_credentials(@options[:azure_credentials]) end |
#azure_host_alias ⇒ Object
113 114 115 116 117 |
# File 'lib/paperclip/storage/azure.rb', line 113 def azure_host_alias @azure_host_alias = @options[:azure_host_alias] @azure_host_alias = @azure_host_alias.call(self) if @azure_host_alias.respond_to?(:call) @azure_host_alias end |
#azure_interface ⇒ Object
156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/paperclip/storage/azure.rb', line 156 def azure_interface @azure_interface ||= begin config = {} [:storage_account_name, :storage_access_key, :container].each do |opt| config[opt] = azure_credentials[opt] if azure_credentials[opt] end obtain_azure_instance_for(config.merge(@azure_options)) end end |
#azure_object(style_name = default_style) ⇒ Object
191 192 193 |
# File 'lib/paperclip/storage/azure.rb', line 191 def azure_object(style_name = default_style) azure_interface.get_blob_properties container_name, path(style_name).sub(%r{\A/},'') end |
#azure_prefixes_in_alias ⇒ Object
119 120 121 |
# File 'lib/paperclip/storage/azure.rb', line 119 def azure_prefixes_in_alias @azure_prefixes_in_alias ||= @options[:azure_prefixes_in_alias].to_i end |
#azure_protocol(style = default_style, with_colon = false) ⇒ Object
123 124 125 126 127 128 129 130 131 132 |
# File 'lib/paperclip/storage/azure.rb', line 123 def azure_protocol(style = default_style, with_colon = false) protocol = @azure_options[:protocol] protocol = protocol.call(style, self) if protocol.respond_to?(:call) if with_colon && !protocol.empty? "#{protocol}:" else protocol.to_s end end |
#azure_uri(style_name = default_style) ⇒ Object
179 180 181 |
# File 'lib/paperclip/storage/azure.rb', line 179 def azure_uri(style_name = default_style) "https://#{azure_base_url}/#{container_name}/#{path(style_name).gsub(%r{\A/}, '')}" end |
#container_name ⇒ Object
150 151 152 153 154 |
# File 'lib/paperclip/storage/azure.rb', line 150 def container_name @container ||= @options[:container] || azure_credentials[:container] @container = @container.call(self) if @container.respond_to?(:call) @container or raise ArgumentError, "missing required :container option" end |
#copy_to_local_file(style, local_dest_path) ⇒ Object
279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
# File 'lib/paperclip/storage/azure.rb', line 279 def copy_to_local_file(style, local_dest_path) log("copying #{path(style)} to local file #{local_dest_path}") blob, content = azure_interface.get_blob(container_name, path(style).sub(%r{\A/},'')) ::File.open(local_dest_path, 'wb') do |local_file| local_file.write(content) end rescue ::Azure::Core::Http::HTTPError => e raise unless e.status_code == 404 warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}") false end |
#create_container ⇒ Object
214 215 216 |
# File 'lib/paperclip/storage/azure.rb', line 214 def create_container azure_interface.create_container container_name end |
#exists?(style = default_style) ⇒ Boolean
202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/paperclip/storage/azure.rb', line 202 def exists?(style = default_style) if original_filename !azure_object(style).nil? else false end rescue ::Azure::Core::Http::HTTPError => e raise unless e.status_code == 404 false end |
#expiring_url(time = 3600, style_name = default_style) ⇒ Object
100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/paperclip/storage/azure.rb', line 100 def expiring_url(time = 3600, style_name = default_style) if path(style_name) signer = ::Azure::Core::Auth::SharedAccessSignature.new( azure_account_name, azure_credentials[:storage_access_key] ) obj_path = path(style_name).gsub(%r{\A/}, '') "#{azure_uri}?#{signer.generate_token(container_name, obj_path, 'r', time)}" else url(style_name) end end |
#flush_deletes ⇒ Object
:nodoc:
266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/paperclip/storage/azure.rb', line 266 def flush_deletes #:nodoc: @queued_for_delete.each do |path| begin log("deleting #{path}") azure_interface.delete_blob container_name, path rescue ::Azure::Core::Http::HTTPError => e raise unless e.status_code == 404 end end @queued_for_delete = [] end |
#flush_writes ⇒ Object
:nodoc:
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 |
# File 'lib/paperclip/storage/azure.rb', line 218 def flush_writes #:nodoc: @queued_for_write.each do |style, file| retries = 0 begin log("saving #{path(style)}") = { content_type: file.content_type, } if azure_container save_blob container_name, path(style).sub(%r{\A/},''), file, end rescue ::Azure::Core::Http::HTTPError => e if e.status_code == 404 create_container retry else raise end ensure file.rewind end end after_flush_writes # allows attachment to clean up temp files @queued_for_write = {} end |
#obtain_azure_instance_for(options) ⇒ Object
168 169 170 171 172 173 174 175 176 177 |
# File 'lib/paperclip/storage/azure.rb', line 168 def obtain_azure_instance_for() if [:use_development_storage] service = ::Azure::Storage::Blob::BlobService.create(use_development_storage: true) else service = ::Azure::Storage::Blob::BlobService.create(storage_account_name: [:storage_account_name], storage_access_key: [:storage_access_key]) end service end |
#parse_credentials(creds) ⇒ Object
195 196 197 198 199 200 |
# File 'lib/paperclip/storage/azure.rb', line 195 def parse_credentials(creds) creds = creds.respond_to?('call') ? creds.call(self) : creds creds = find_credentials(creds).stringify_keys env = Object.const_defined?(:Rails) ? Rails.env : nil (creds[env] || creds).symbolize_keys end |
#save_blob(container_name, storage_path, file, write_options) ⇒ Object
248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/paperclip/storage/azure.rb', line 248 def save_blob(container_name, storage_path, file, ) if file.size < 64.megabytes azure_interface.create_block_blob container_name, storage_path, file.read, else blocks = []; count = 0 while data = file.read(4.megabytes) block_id = "block_#{(count += 1).to_s.rjust(5, '0')}" azure_interface.put_blob_block container_name, storage_path, block_id, data blocks << [block_id] end azure_interface.commit_blob_blocks container_name, storage_path, blocks end end |