class AzureBlob::Client
def append_blob_block(key, content, options = {})
def append_blob_block(key, content, options = {}) uri = generate_uri("#{container}/#{key}") uri.query = URI.encode_www_form(comp: "appendblock") headers = { "Content-Length": content.size, "Content-Type": options[:content_type], "Content-MD5": options[:content_md5], } Http.new(uri, headers, signer:).put(content) end
def commit_blob_blocks(key, block_ids, options = {})
def commit_blob_blocks(key, block_ids, options = {}) block_list = BlockList.new(block_ids) content = block_list.to_s uri = generate_uri("#{container}/#{key}") uri.query = URI.encode_www_form(comp: "blocklist") headers = { "Content-Length": content.size, "Content-Type": options[:content_type], "Content-MD5": options[:content_md5], "x-ms-blob-content-disposition": options[:content_disposition], } Http.new(uri, headers, metadata: options[:metadata], signer:).put(content) end
def create_append_blob(key, options = {})
def create_append_blob(key, options = {}) uri = generate_uri("#{container}/#{key}") headers = { "x-ms-blob-type": "AppendBlob", "Content-Length": 0, "Content-Type": options[:content_type], "Content-MD5": options[:content_md5], "x-ms-blob-content-disposition": options[:content_disposition], } Http.new(uri, headers, metadata: options[:metadata], signer:).put(nil) end
def create_block_blob(key, content, options = {})
def create_block_blob(key, content, options = {}) if content.size > (options[:block_size] || DEFAULT_BLOCK_SIZE) put_blob_multiple(key, content, **options) else put_blob_single(key, content, **options) end end
def delete_blob(key, options = {})
def delete_blob(key, options = {}) uri = generate_uri("#{container}/#{key}") headers = { "x-ms-delete-snapshots": options[:delete_snapshots] || "include", } Http.new(uri, headers, signer:).delete end
def delete_prefix(prefix, options = {})
def delete_prefix(prefix, options = {}) results = list_blobs(prefix:) results.each { |key| delete_blob(key) } end
def generate_block_id(index)
def generate_block_id(index) Base64.urlsafe_encode64(index.to_s.rjust(6, "0")) end
def generate_uri(path)
def generate_uri(path) URI.parse(URI::DEFAULT_PARSER.escape(File.join(host, path))) end
def get_blob(key, options = {})
def get_blob(key, options = {}) uri = generate_uri("#{container}/#{key}") headers = { "x-ms-range": options[:start] && "bytes=#{options[:start]}-#{options[:end]}", } Http.new(uri, headers, signer:).get end
def get_blob_properties(key, options = {})
def get_blob_properties(key, options = {}) uri = generate_uri("#{container}/#{key}") response = Http.new(uri, signer:).head Blob.new(response) end
def host
def host "https://#{account_name}.blob.core.windows.net" end
def initialize(account_name:, access_key:, container:)
def initialize(account_name:, access_key:, container:) @account_name = account_name @container = container @signer = Signer.new(account_name:, access_key:) end
def list_blobs(options = {})
def list_blobs(options = {}) uri = generate_uri(container) query = { comp: "list", restype: "container", prefix: options[:prefix].to_s.gsub(/\\/, "/"), } query[:maxresults] = options[:max_results] if options[:max_results] uri.query = URI.encode_www_form(**query) fetcher = ->(marker) do query[:marker] = marker query.reject! { |key, value| value.to_s.empty? } uri.query = URI.encode_www_form(**query) response = Http.new(uri, signer:).get end BlobList.new(fetcher) end
def put_blob_block(key, index, content, options = {})
def put_blob_block(key, index, content, options = {}) block_id = generate_block_id(index) uri = generate_uri("#{container}/#{key}") uri.query = URI.encode_www_form(comp: "block", blockid: block_id) headers = { "Content-Length": content.size, "Content-Type": options[:content_type], "Content-MD5": options[:content_md5], } Http.new(uri, headers, signer:).put(content) block_id end
def put_blob_multiple(key, content, options = {})
def put_blob_multiple(key, content, options = {}) content = StringIO.new(content) if content.is_a? String block_size = options[:block_size] || DEFAULT_BLOCK_SIZE block_count = (content.size.to_f / block_size).ceil block_ids = block_count.times.map do |i| put_blob_block(key, i, content.read(block_size)) end commit_blob_blocks(key, block_ids, options) end
def put_blob_single(key, content, options = {})
def put_blob_single(key, content, options = {}) content = StringIO.new(content) if content.is_a? String uri = generate_uri("#{container}/#{key}") headers = { "x-ms-blob-type": "BlockBlob", "Content-Length": content.size, "Content-Type": options[:content_type], "Content-MD5": options[:content_md5], "x-ms-blob-content-disposition": options[:content_disposition], } Http.new(uri, headers, metadata: options[:metadata], signer:).put(content.read) end
def signed_uri(key, permissions:, expiry:, **options)
def signed_uri(key, permissions:, expiry:, **options) uri = generate_uri("#{container}/#{key}") uri.query = signer.sas_token(uri, permissions:, expiry:, **options) uri end