Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Microsoft Azure storage support * Add support for Microsoft Azure Storage * Comply with the new headers implementation
- Loading branch information
Showing
7 changed files
with
198 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
require "active_support/core_ext/numeric/bytes" | ||
require "azure/storage" | ||
require "azure/storage/core/auth/shared_access_signature" | ||
|
||
# Wraps the Microsoft Azure Storage Blob Service as a Active Storage service. | ||
# See `ActiveStorage::Service` for the generic API documentation that applies to all services. | ||
class ActiveStorage::Service::AzureService < ActiveStorage::Service | ||
attr_reader :client, :path, :blobs, :container, :signer | ||
|
||
def initialize(path:, storage_account_name:, storage_access_key:, container:) | ||
@client = Azure::Storage::Client.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key) | ||
@signer = Azure::Storage::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key) | ||
@blobs = client.blob_client | ||
@container = container | ||
@path = path | ||
end | ||
|
||
def upload(key, io, checksum: nil) | ||
instrument :upload, key, checksum: checksum do | ||
begin | ||
blobs.create_block_blob(container, key, io, content_md5: checksum) | ||
rescue Azure::Core::Http::HTTPError => e | ||
raise ActiveStorage::IntegrityError | ||
end | ||
end | ||
end | ||
|
||
def download(key) | ||
if block_given? | ||
instrument :streaming_download, key do | ||
stream(key, &block) | ||
end | ||
else | ||
instrument :download, key do | ||
_, io = blobs.get_blob(container, key) | ||
io.force_encoding(Encoding::BINARY) | ||
end | ||
end | ||
end | ||
|
||
def delete(key) | ||
instrument :delete, key do | ||
begin | ||
blobs.delete_blob(container, key) | ||
rescue Azure::Core::Http::HTTPError | ||
false | ||
end | ||
end | ||
end | ||
|
||
def exist?(key) | ||
instrument :exist, key do |payload| | ||
answer = blob_for(key).present? | ||
payload[:exist] = answer | ||
answer | ||
end | ||
end | ||
|
||
def url(key, expires_in:, disposition:, filename:) | ||
instrument :url, key do |payload| | ||
base_url = url_for(key) | ||
generated_url = signer.signed_uri(URI(base_url), false, permissions: "r", | ||
expiry: format_expiry(expires_in), content_disposition: "#{disposition}; filename=\"#{filename}\"").to_s | ||
|
||
payload[:url] = generated_url | ||
|
||
generated_url | ||
end | ||
end | ||
|
||
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) | ||
instrument :url, key do |payload| | ||
base_url = url_for(key) | ||
generated_url = signer.signed_uri(URI(base_url), false, permissions: "rw", | ||
expiry: format_expiry(expires_in)).to_s | ||
|
||
payload[:url] = generated_url | ||
|
||
generated_url | ||
end | ||
end | ||
|
||
def headers_for_direct_upload(key, content_type:, checksum:, **) | ||
{ "Content-Type" => content_type, "Content-MD5" => checksum, "x-ms-blob-type" => "BlockBlob" } | ||
end | ||
|
||
private | ||
def url_for(key) | ||
"#{path}/#{container}/#{key}" | ||
end | ||
|
||
def blob_for(key) | ||
blobs.get_blob_properties(container, key) | ||
rescue Azure::Core::Http::HTTPError | ||
false | ||
end | ||
|
||
def format_expiry(expires_in) | ||
expires_in ? Time.now.utc.advance(seconds: expires_in).iso8601 : nil | ||
end | ||
|
||
# Reads the object for the given key in chunks, yielding each to the block. | ||
def stream(key, options = {}, &block) | ||
blob = blob_for(key) | ||
|
||
chunk_size = 5.megabytes | ||
offset = 0 | ||
|
||
while offset < blob.properties[:content_length] | ||
_, io = blobs.get_blob(container, key, start_range: offset, end_range: offset + chunk_size - 1) | ||
yield io | ||
offset += chunk_size | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
require "service/shared_service_tests" | ||
require "httparty" | ||
require "uri" | ||
|
||
if SERVICE_CONFIGURATIONS[:azure] | ||
class ActiveStorage::Service::AzureServiceTest < ActiveSupport::TestCase | ||
SERVICE = ActiveStorage::Service.configure(:azure, SERVICE_CONFIGURATIONS) | ||
|
||
include ActiveStorage::Service::SharedServiceTests | ||
end | ||
|
||
else | ||
puts "Skipping Azure Storage Service tests because no Azure configuration was supplied" | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters