<%@ Page Title="Strategy: Direct-to-Azure Blob" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" %> 
<%@ Import Namespace="AjaxUploader.Providers" %> 
<asp:Content ID="TitleContent" ContentPlaceHolderID="TitleContent" runat="server">Direct-to-Azure Blob - AjaxUploader v5.3 Demos</asp:Content> 
<asp:Content ID="HeaderContent" ContentPlaceHolderID="HeaderTitle" runat="server">Direct-to-Azure Blob</asp:Content> 
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server"> 
<% var azureSignerReady = AjaxUploader.Providers.Azure.Signer != null; %> 
<div class="demo-card"> <div class="demo-card-header">Browser-direct Azure Blob upload</div> <div class="demo-card-body"> <p class="demo-description"> The browser uploads each block via <code>PutBlock</code> against a pre-signed blob SAS URL, then finalises with a single <code>PutBlockList</code>. Mirrors the S3 pattern - your server signs once and the bytes bypass IIS entirely. IndexedDB resume preserves the upload session + already-committed block IDs so reloads pick up where they stopped.
 </p> <div class="demo-expect"><strong>What you should see:</strong> 4&nbsp;MB blocks PUT directly to Azure Blob Storage using server-issued signatures - bytes never transit IIS. Resumable via IndexedDB.</div> <% if (!azureSignerReady) { %> <div class="alert alert-warning mt-3" role="alert"> <strong>Setup required.</strong> No <code>IAzureSigner</code> is registered yet, so <code>/ajaxupload.axd/azure/*</code> currently returns 501. To activate this demo:
 <ol class="mt-2 mb-0"> <li>Install <code>Azure.Storage.Blobs</code> into <code>bin/</code> (from NuGet).</li> <li>Move <code>v5-signers/DemoAzureSigner.cs.template</code> -> <code>App_Code/DemoAzureSigner.cs</code>.</li> <li>Fill in <code>DemoAzure:*</code> values in <code>web.config</code>.</li> <li>Recycle the app pool.</li> </ol> Full walk-through: <a href="<%= ResolveUrl("~/v5-signers/README.txt") %>"><code>v5-signers/README.txt</code></a>.
 </div> <% } else { %> <div class="alert alert-success mt-3" role="alert"> <strong>Signer active.</strong> <code>IAzureSigner</code> is registered (<code><%= Server.HtmlEncode(AjaxUploader.Providers.Azure.Signer.GetType().Name) %></code>). The uploader below will upload directly to your container.
 </div> <div class="d-flex gap-2 mb-2 mt-4">
 <button type="button" class="btn btn-primary" id="startBtn_azureUploader">Start upload</button>
 <button type="button" class="btn btn-outline-secondary" id="clearBtn_azureUploader">Clear queue</button>
 </div>
 <div id="azureUploader"></div> <script> (function () {
 function init() {
 if (!window.AjaxUploader || typeof AjaxUploader.create !== 'function') return; // graceful no-op if runtime missing
 var startButton = document.getElementById('startBtn_azureUploader');
 var clearButton = document.getElementById('clearBtn_azureUploader');
 var uploader = AjaxUploader.create('#azureUploader', {
 uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
 strategy: 'azure',
 chunkSize: 4 * 1024 * 1024,
 chunkConcurrency: 4,
 autoUpload: false,
 multiple: true,
 persistState: true,
 persistAdapter: 'indexeddb'
 });
 if (startButton) startButton.addEventListener('click', function () { if (uploader && typeof uploader.upload === 'function') uploader.upload(); });
 if (clearButton) clearButton.addEventListener('click', function () { if (uploader && typeof uploader.clear === 'function') uploader.clear(); });
 }
 if (document.readyState !== 'loading') init();
 else document.addEventListener('DOMContentLoaded', init);
 })();
 </script> <% } %> <h5 class="mt-4">Client config</h5> <div class="code-panel"> 
<pre><code>AjaxUploader.create('#uploader', {
 uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
 strategy: 'azure',
 chunkSize: 4 * 1024 * 1024, <span style="color:#94a3b8">// Azure block size</span> chunkConcurrency: 4,
 persistState: true,
 persistAdapter: 'indexeddb'
});</code></pre> </div> <h5 class="mt-4">Server-side (Global.asax.cs)</h5> <div class="code-panel"> 
<pre><code>using AjaxUploader.Providers;

protected void Application_Start(object sender, EventArgs e)
{
 <span style="color:#94a3b8">// Your IAzureSigner wraps BlobServiceClient and hands out SAS URLs.</span> Azure.Signer = new MyAzureSigner(
 connectionString: ConfigurationManager.ConnectionStrings["AzureStorage"].ConnectionString,
 containerName: "uploads");
}</code></pre> </div> <h5 class="mt-4">Wire protocol</h5> <ul class="mb-3"> <li><code>POST /ajaxupload.axd/azure/create</code> -> <code>{ uploadId, blobUrl, blockIdPrefix }</code></li> <li><code>PUT {blobUrl}&amp;comp=block&amp;blockid=...</code> per block (browser-direct)</li> <li><code>PUT {blobUrl}&amp;comp=blocklist</code> with XML BlockList body (browser-direct)</li> <li><code>POST /ajaxupload.axd/azure/complete</code> to record metadata server-side</li> </ul> <h5 class="mt-4">Container CORS</h5> <div class="code-panel"> 
<pre><code>az storage cors add \
 --methods PUT \
 --origins https://ajaxuploader.com \
 --allowed-headers "*" \
 --exposed-headers "*" \
 --services b \
 --max-age 3600</code></pre> </div> </div> 
</div> 
</asp:Content> 
