This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan
<p>My daughter is attending nursery school and every day they post photos of the baby to a special portal so we can see what she has been doing. The web site, is, err, well... functional. However they block the ability to download the photos in their UI, I've no clue why, but it's super frustrating.</p>
<p>I love <a href="https://paul.kinlan.me/tags/bookmarklets/">Bookmarklet</a> because they let me quickly augment a site without having to build a full extension, and this is no exception. I knew that the UI gave me access to the actual image so it shouldn't be too complex to find those images, fetch them and save them.</p>
<p>Traditionally saving a file from the browser to your machine is <a href="https://www.youtube.com/watch?v=rXLNC8yCRnw&feature=player_detailpage#t=280s">hacky</a>, you create an anchor that links to the image, add a 'download' attribute (optionally with the name of the file), and then simulate a mouse click on it. </p>
<p>I knew that Chromium had added a new <a href="https://web.dev/file-system-access/">File System API</a>, so I wanted to try my hand to see if it would let me batch download a large array of files and then save them to a directory, and it does.</p>
<p>The process I chose was as follows:</p>
<p>Request the users consent to access a folder (it won't let you pick a system folder such as the photo directory) and get access to the directory handle.</p>
<ol>
<li>Find all the images on the page</li>
<li>Sequentially fetch the images (could have made it parallel but I didn't want to overload their server)</li>
<li>Create a file handle in the directory that the user gave the script access to</li>
<li>Create a file writer stream</li>
<li>Pipe the image to it.</li>
<li>Voila. It's saved to disk.</li>
</ol>
<p>The code for that process is as follows:</p>
<div class="highlight"><pre style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-JavaScript" data-lang="JavaScript"><span style="color:#66d9ef">const</span> <span style="color:#a6e22e">run</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">async</span> () => {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">dirHandle</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> window.<span style="color:#a6e22e">showDirectoryPicker</span>();
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">imgs</span> <span style="color:#f92672">=</span> document.<span style="color:#a6e22e">querySelectorAll</span>(<span style="color:#e6db74">"img"</span>);
<span style="color:#66d9ef">let</span> <span style="color:#a6e22e">i</span> <span style="color:#f92672">=</span> <span style="color:#ae81ff">0</span>;
<span style="color:#a6e22e">imgs</span>.<span style="color:#a6e22e">forEach</span>(<span style="color:#a6e22e">async</span> (<span style="color:#a6e22e">img</span>) => {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">url</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">img</span>.<span style="color:#a6e22e">src</span>;
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">name</span> <span style="color:#f92672">=</span> <span style="color:#e6db74">`img-</span><span style="color:#e6db74">${</span><span style="color:#a6e22e">i</span><span style="color:#e6db74">}</span><span style="color:#e6db74">.png`</span>;
<span style="color:#a6e22e">i</span><span style="color:#f92672">++</span>;
<span style="color:#66d9ef">try</span> {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">`Fetching </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">url</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">response</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">fetch</span>(<span style="color:#a6e22e">url</span>);
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">`Saving to </span><span style="color:#e6db74">${</span><span style="color:#a6e22e">name</span><span style="color:#e6db74">}</span><span style="color:#e6db74">`</span>);
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">file</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">dirHandle</span>.<span style="color:#a6e22e">getFileHandle</span>(<span style="color:#a6e22e">name</span>, { <span style="color:#a6e22e">create</span><span style="color:#f92672">:</span> <span style="color:#66d9ef">true</span> });
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">writable</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">file</span>.<span style="color:#a6e22e">createWritable</span>();
<span style="color:#a6e22e">await</span> <span style="color:#a6e22e">response</span>.<span style="color:#a6e22e">body</span>.<span style="color:#a6e22e">pipeTo</span>(<span style="color:#a6e22e">writable</span>);
} <span style="color:#66d9ef">catch</span> (<span style="color:#a6e22e">err</span>) {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">err</span>);
}
});
};
<span style="color:#a6e22e">run</span>();
</code></pre></div><p>I then wrapped it up in a bookmarklet, and now I can download all the images directly to a directory of my choosing, and we are very happy that we can have permanent access to these memories :D</p>
This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan