<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Azure &#8211; Other Things</title>
	<atom:link href="https://blog.adamzolo.com/tag/azure/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.adamzolo.com</link>
	<description>Blog about Things by Adam Zolotarev</description>
	<lastBuildDate>Tue, 02 Jun 2020 19:57:22 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>
	<item>
		<title>Using Azurite with Active Storage</title>
		<link>https://blog.adamzolo.com/using-azurite-with-active-storage/</link>
					<comments>https://blog.adamzolo.com/using-azurite-with-active-storage/#respond</comments>
		
		<dc:creator><![CDATA[Adam Zolo]]></dc:creator>
		<pubDate>Tue, 02 Jun 2020 19:57:22 +0000</pubDate>
				<category><![CDATA[Active Storage]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Azure]]></category>
		<category><![CDATA[Azurite]]></category>
		<guid isPermaLink="false">http://blog.adamzolo.com/?p=919</guid>

					<description><![CDATA[Install Azurite in your preferred way: npm install azurite Install Microsoft Azure Storage Explorer Create some directory to run azurite from: `~/azurite` Add storage.yml configuration for azurite (using the default dev account and key): Update development.rb to use azurite_emulator: Start azurite from the directory you created for azurite: azurite --location ~/azurite --debug ~/azurite/debug.log Start Azure&#8230;<p><a class="more-link" href="https://blog.adamzolo.com/using-azurite-with-active-storage/" title="Continue reading &#8216;Using Azurite with Active Storage&#8217;">Continue reading <span class="meta-nav">&#8594;</span></a></p>]]></description>
										<content:encoded><![CDATA[
<p>Install <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-use-azurite" target="_blank" rel="noreferrer noopener">Azurite</a> in your preferred way: <code>npm install azurite</code></p>



<p>Install <a href="https://azure.microsoft.com/en-us/features/storage-explorer/" target="_blank" rel="noreferrer noopener">Microsoft Azure Storage Explorer</a></p>



<p>Create some directory to run azurite from: `~/azurite`</p>



<p>Add <code>storage.yml</code> configuration for azurite (using the default dev account and key):</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: yaml; title: ; notranslate">
azurite_emulator:
  service: AzureStorage
  storage_account_name: &#039;devstoreaccount1&#039;
  storage_access_key: &#039;Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==&#039;
  container: &#039;container-name&#039;
  storage_blob_host: &#039;http://127.0.0.1:10000/devstoreaccount1&#039;
</pre></div>


<p></p>



<p>Update <code>development.rb</code> to use azurite_emulator: </p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: ruby; title: ; notranslate">
config.active_storage.service = :azurite_emulator
</pre></div>


<p></p>



<p>Start azurite from the directory you created for azurite:  <code>azurite --location ~/azurite --debug ~/azurite/debug.log</code></p>



<p>Start Azure Storage Explorer, connect to local emulator, and create <code>container-name</code> blob container &#8211; the same container name you specified in the <code>storage.yml</code> file.</p>



<p>Start uploading to Azurite.</p>



<h2 class="wp-block-heading">Note for Rails 5.2</h2>



<p>Some changes have not been backported as of this post, and you have to monkey-patch ActiveStorage file as described here &#8211; <a href="http://www.garytaylor.blog/index.php/2019/01/30/rails-active-storage-and-azure-beyond-config/">http://www.garytaylor.blog/index.php/2019/01/30/rails-active-storage-and-azure-beyond-config/</a> &#8211; this allows us to work with azurite locally.</p>



<p></p>



<p>If you want to use the newer <code>azure-storage-blob</code> instead of the deprecated <code>azure-storage</code> and you&#8217;re on Rails 5.2, you have to do a bit more monkey-patching &#8211; otherwise, you&#8217;ll start getting <a href="https://stackoverflow.com/questions/62045971/no-such-file-to-load-azure-storage-rb">No such file to load — azure/storage.rb</a>&#8220;:</p>



<p>Add two empty files: <code>lib/azure/storage/core/auth/shared_access_signature.rb</code>, and <code>lib/azure/storage.rb</code></p>



<p>Add this to config/initializers/active_storage_6_patch.rb (this is the current master version of the ActiveStorage module):</p>


<div class="wp-block-syntaxhighlighter-code "><pre class="brush: ruby; title: ; notranslate">
require &quot;azure/storage/blob&quot;
require &#039;active_storage/service/azure_storage_service&#039;
module ActiveStorage
  # Wraps the Microsoft Azure Storage Blob Service as an Active Storage service.
  # See ActiveStorage::Service for the generic API documentation that applies to all services.
  class Service::AzureStorageService &lt; Service
    attr_reader :client, :container, :signer

    def initialize(storage_account_name:, storage_access_key:, container:, public: false, **options)
      @client = Azure::Storage::Blob::BlobService.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key, **options)
      @signer = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
      @container = container
      @public = public
    end

    def upload(key, io, checksum: nil, filename: nil, content_type: nil, disposition: nil, **)
      instrument :upload, key: key, checksum: checksum do
        handle_errors do
          content_disposition = content_disposition_with(filename: filename, type: disposition) if disposition &amp;&amp; filename

          client.create_block_blob(container, key, IO.try_convert(io) || io, content_md5: checksum, content_type: content_type, content_disposition: content_disposition)
        end
      end
    end

    def download(key, &amp;block)
      if block_given?
        instrument :streaming_download, key: key do
          stream(key, &amp;block)
        end
      else
        instrument :download, key: key do
          handle_errors do
            _, io = client.get_blob(container, key)
            io.force_encoding(Encoding::BINARY)
          end
        end
      end
    end

    def download_chunk(key, range)
      instrument :download_chunk, key: key, range: range do
        handle_errors do
          _, io = client.get_blob(container, key, start_range: range.begin, end_range: range.exclude_end? ? range.end - 1 : range.end)
          io.force_encoding(Encoding::BINARY)
        end
      end
    end

    def delete(key)
      instrument :delete, key: key do
        client.delete_blob(container, key)
      rescue Azure::Core::Http::HTTPError =&gt; e
        raise unless e.type == &quot;BlobNotFound&quot;
        # Ignore files already deleted
      end
    end

    def delete_prefixed(prefix)
      instrument :delete_prefixed, prefix: prefix do
        marker = nil

        loop do
          results = client.list_blobs(container, prefix: prefix, marker: marker)

          results.each do |blob|
            client.delete_blob(container, blob.name)
          end

          break unless marker = results.continuation_token.presence
        end
      end
    end

    def exist?(key)
      instrument :exist, key: key do |payload|
        answer = blob_for(key).present?
        payload&#x5B;:exist] = answer
        answer
      end
    end

    def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
      instrument :url, key: key do |payload|
        generated_url = signer.signed_uri(
          uri_for(key), false,
          service: &quot;b&quot;,
          permissions: &quot;rw&quot;,
          expiry: format_expiry(expires_in)
        ).to_s

        payload&#x5B;:url] = generated_url

        generated_url
      end
    end

    def headers_for_direct_upload(key, content_type:, checksum:, filename: nil, disposition: nil, **)
      content_disposition = content_disposition_with(type: disposition, filename: filename) if filename

      { &quot;Content-Type&quot; =&gt; content_type, &quot;Content-MD5&quot; =&gt; checksum, &quot;x-ms-blob-content-disposition&quot; =&gt; content_disposition, &quot;x-ms-blob-type&quot; =&gt; &quot;BlockBlob&quot; }
    end

    private
      def private_url(key, expires_in:, filename:, disposition:, content_type:, **)
        signer.signed_uri(
          uri_for(key), false,
          service: &quot;b&quot;,
          permissions: &quot;r&quot;,
          expiry: format_expiry(expires_in),
          content_disposition: content_disposition_with(type: disposition, filename: filename),
          content_type: content_type
        ).to_s
      end

      def public_url(key, **)
        uri_for(key).to_s
      end


      def uri_for(key)
        client.generate_uri(&quot;#{container}/#{key}&quot;)
      end

      def blob_for(key)
        client.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)
        blob = blob_for(key)

        chunk_size = 5.megabytes
        offset = 0

        raise ActiveStorage::FileNotFoundError unless blob.present?

        while offset &lt; blob.properties&#x5B;:content_length]
          _, chunk = client.get_blob(container, key, start_range: offset, end_range: offset + chunk_size - 1)
          yield chunk.force_encoding(Encoding::BINARY)
          offset += chunk_size
        end
      end

      def handle_errors
        yield
      rescue Azure::Core::Http::HTTPError =&gt; e
        case e.type
        when &quot;BlobNotFound&quot;
          raise ActiveStorage::FileNotFoundError
        when &quot;Md5Mismatch&quot;
          raise ActiveStorage::IntegrityError
        else
          raise
        end
      end
  end
end
</pre></div>]]></content:encoded>
					
					<wfw:commentRss>https://blog.adamzolo.com/using-azurite-with-active-storage/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
