<%@ Page Title="IndexedDB Resume (Golden Retriever)" Language="C#" MasterPageFile="~/Site.master" AutoEventWireup="true" %> 
<asp:Content ID="TitleContent" ContentPlaceHolderID="TitleContent" runat="server">IndexedDB Resume - AjaxUploader v5.3 Demos</asp:Content> 
<asp:Content ID="HeaderContent" ContentPlaceHolderID="HeaderTitle" runat="server">IndexedDB Resume (Golden Retriever)</asp:Content> 
<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server"> 
<div class="demo-card"> <div class="demo-card-header">Uploads that survive reloads + crashes</div> <div class="demo-card-body"> <p class="demo-description"> Every in-flight upload persists to IndexedDB automatically. With <code>persistBlobs: true</code>, File objects are stored too - uploads auto-resume after tab crashes or page reloads without re-picking files. Works with <code>chunked</code>, <code>s3</code>, and <code>tus</code> strategies: each resume preserves the server session (chunk offsets, S3 part ETags, or tus URL) so the resumed task continues from exactly where it stopped.
 </p> <div class="demo-expect"><strong>What you should see:</strong> start a chunked upload (1&nbsp;MB chunks), reload the page mid-flight, and it resumes from the last completed chunk - state persisted in IndexedDB and verified against the server first.</div> <div class="d-flex gap-2 mb-2 mt-4">
 <button type="button" class="btn btn-primary" id="startBtn_resumeUploader">Start upload</button>
 <button type="button" class="btn btn-outline-secondary" id="clearBtn_resumeUploader">Clear queue</button>
 </div>
 <div id="resumeUploader"></div> <script> (function () {
 function init() {
 if (!window.AjaxUploader || typeof AjaxUploader.create !== 'function') return; // graceful no-op if runtime missing
 var startButton = document.getElementById('startBtn_resumeUploader');
 var clearButton = document.getElementById('clearBtn_resumeUploader');
 var uploader = AjaxUploader.create('#resumeUploader', {
 uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
 chunkUrl: '<%= ResolveUrl("~/ajaxupload.axd/chunk") %>',
 chunkCompleteUrl: '<%= ResolveUrl("~/ajaxupload.axd/chunk/complete") %>',
 chunked: true,
 chunkSize: 1 * 1024 * 1024,
 chunkConcurrency: 3,
 autoUpload: false,
 multiple: true,
 persistState: true,
 persistAdapter: 'indexeddb',
 persistBlobs: true
 });
 uploader.on('stateRestored', function (state) {
 if (state.resumableTasks && state.resumableTasks.length) {
 state.resumableTasks.forEach(function (entry) {
 try { uploader.resumeFromState(entry); } catch (e) { /* user will re-pick */ }
 });
 }
 });
 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> <div class="alert alert-info mt-3" role="alert"> <strong>Try it:</strong> pick a large file, start uploading, reload the page (F5) before it finishes. The task auto-rehydrates from IndexedDB and continues from the last completed chunk.
 </div> <h5 class="mt-4">Configuration</h5> <div class="code-panel"> 
<pre><code>AjaxUploader.create('#uploader', {
 uploadUrl: '<%= ResolveUrl("~/ajaxupload.axd/upload") %>',
 chunkUrl: '<%= ResolveUrl("~/ajaxupload.axd/chunk") %>',
 chunkCompleteUrl: '<%= ResolveUrl("~/ajaxupload.axd/chunk/complete") %>',
 chunked: true,
 persistState: true,
 persistAdapter: 'indexeddb', <span style="color:#94a3b8">// or 'localStorage'</span> persistBlobs: true <span style="color:#94a3b8">// store File blobs for zero-click resume</span> 
});</code></pre> </div> <h5 class="mt-4">Rehydrate on load</h5> <div class="code-panel"> 
<pre><code>uploader.on('stateRestored', function (state) {
 state.resumableTasks.forEach(function (entry) {
 <span style="color:#94a3b8">// entry.blob present when persistBlobs=true; else pass a re-picked File:</span> uploader.resumeFromState(entry /* , file */);
 });
});</code></pre> </div> <h5 class="mt-4">What gets persisted</h5> <ul class="mb-3"> <li><strong>Always:</strong> task id, file metadata, strategy, progress, hash, per-chunk byte counts, S3 <code>uploadId</code> + part ETags, tus session URL.</li> <li><strong>With <code>persistBlobs: true</code>:</strong> the File object itself (IndexedDB stores Blobs natively).</li> <li><strong>Never:</strong> upload tokens with limited lifetimes - those are re-fetched on resume.</li> </ul> </div> 
</div> 
</asp:Content> 
