This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan
<p>I've frequently said that for web apps to compete effectively in the world of
apps, they need to be integrated in to all of the places that users expect apps
to be. Inter-app communication is one of the major missing pieces of the web
platform, and specifically one of the last major missing features is native
level sharing: Web apps need to be able to get <a href="https://paul.kinlan.me/unintended-silos/">data out of their
silo</a> and into other web sites and apps; they also need to
be able to receive the data from other native apps and sites.</p>
<p>The File Share Target API is a game-changer of an API that is now in Chrome
Canary. The API extends the <a href="https://github.com/WICG/web-share-target/blob/master/docs/explainer.md">Web Share Target
API</a>
that lets apps and sites share simple links and text to web sites by integrating
them into the systems sharing functionality.</p>
<p>This very static file blog utilizes the Web Share Target API so I can quickly
<a href="https://paul.kinlan.me/web-share-target-api/">share links</a> that I find interesting to it from any
Android application, and as of last week <a href="https://paul.kinlan.me/testing-file-share-target-from-camera/">I enabled the File Share Target API so
that I can upload images to my blog directly from the Camera app on
Android</a>. This post is all about how I
did it (and stole some code from Jake Archibald — tbf he worked out a lot
of the bugs for an integration they are doing in to
<a href="https://squoosh.app/">squoosh.app</a>.)</p>
<p>The <a href="https://wicg.github.io/web-share-target/level-2/#example-3-manifest-webmanifest">File Share Target
API</a>
is a very novel API in that it is fully progressive. If your application can
handle Form <code>POST</code> requests then you can integrate easily with this API. The
basic flow is: when the user chooses your application from the native picker,
Chrome will send a Form <code>POST</code> request to your server, it is up to you what you
do with it (handle in a service worker or on the server).</p>
<p>To add support for sharing files into your web app you need to do two things:</p>
<ol>
<li>Declare support for sharing files via the manifest file,</li>
<li>Handle the Form <code>POST</code> request in your Service Worker.</li>
</ol>
<p>The manifest declares to the host system how Sharing should be mapped from the
host application to the web app. In the manifest below it essentially says "When
a user shares a file of type 'image/*' make a Form POST request to
'/share/image/' and name the data 'file'".</p>
<p><em>manifest.json</em></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-JSON" data-lang="JSON">{
<span style="color:#f92672">"name"</span>: <span style="color:#e6db74">"Blog: Share Image"</span>,
<span style="color:#f92672">"short_name"</span>: <span style="color:#e6db74">"Blog: Share Image"</span>,
<span style="color:#f92672">"start_url"</span>: <span style="color:#e6db74">"/share/image/"</span>,
<span style="color:#f92672">"theme_color"</span>: <span style="color:#e6db74">"#000000"</span>,
<span style="color:#f92672">"background_color"</span>: <span style="color:#e6db74">"#000000"</span>,
<span style="color:#f92672">"icons"</span>: [ {
<span style="color:#f92672">"sizes"</span>: <span style="color:#e6db74">"192x192"</span>,
<span style="color:#f92672">"src"</span>: <span style="color:#e6db74">"/images/me.png"</span>,
<span style="color:#f92672">"type"</span>: <span style="color:#e6db74">"image/png"</span>
}],
<span style="color:#f92672">"share_target"</span>: {
<span style="color:#f92672">"action"</span>: <span style="color:#e6db74">"/share/image/"</span>,
<span style="color:#f92672">"method"</span>: <span style="color:#e6db74">"POST"</span>,
<span style="color:#f92672">"enctype"</span>: <span style="color:#e6db74">"multipart/form-data"</span>,
<span style="color:#f92672">"params"</span>: {
<span style="color:#f92672">"files"</span>: [
{
<span style="color:#f92672">"name"</span>: <span style="color:#e6db74">"file"</span>,
<span style="color:#f92672">"accept"</span>: [<span style="color:#e6db74">"image/*"</span>]
}
]
}
},
<span style="color:#f92672">"display"</span>: <span style="color:#e6db74">"standalone"</span>,
<span style="color:#f92672">"scope"</span>: <span style="color:#e6db74">"/share/"</span>
}
</code></pre></div><p>Once the user shares to your web application, Chrome will make the web request
to your site with the file data as the payload.</p>
<p>It is recommended that you handle the POST request inside your service worker so
that 1) it is fast, 2) resilient to the network not being available. You can do
this as follows:</p>
<p><em>serviceworker.js</em> - <a href="https://paul.kinlan.me/share/image/sw.js">demo</a></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:#a6e22e">onfetch</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">async</span> (<span style="color:#a6e22e">event</span>) => {
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">request</span>.<span style="color:#a6e22e">method</span> <span style="color:#f92672">!==</span> <span style="color:#e6db74">'POST'</span>) <span style="color:#66d9ef">return</span>;
<span style="color:#66d9ef">if</span> (<span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">request</span>.<span style="color:#a6e22e">url</span>.<span style="color:#a6e22e">startsWith</span>(<span style="color:#e6db74">'https://paul.kinlan.me/share/image/'</span>) <span style="color:#f92672">===</span> <span style="color:#66d9ef">false</span>) <span style="color:#66d9ef">return</span>;
<span style="color:#75715e">/* This is to fix the issue Jake found */</span>
<span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">respondWith</span>(<span style="color:#a6e22e">Response</span>.<span style="color:#a6e22e">redirect</span>(<span style="color:#e6db74">'/share/image/'</span>));
<span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">waitUntil</span>(<span style="color:#a6e22e">async</span> <span style="color:#66d9ef">function</span> () {
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">data</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">request</span>.<span style="color:#a6e22e">formData</span>();
<span style="color:#66d9ef">const</span> <span style="color:#a6e22e">client</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">await</span> <span style="color:#a6e22e">self</span>.<span style="color:#a6e22e">clients</span>.<span style="color:#a6e22e">get</span>(<span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">resultingClientId</span> <span style="color:#f92672">||</span> <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">clientId</span>);
<span style="color:#75715e">// Get the data from the named element 'file'
</span><span style="color:#75715e"></span> <span style="color:#66d9ef">const</span> <span style="color:#a6e22e">file</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">get</span>(<span style="color:#e6db74">'file'</span>);
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#e6db74">'file'</span>, <span style="color:#a6e22e">file</span>);
<span style="color:#a6e22e">client</span>.<span style="color:#a6e22e">postMessage</span>({ <span style="color:#a6e22e">file</span>, <span style="color:#a6e22e">action</span><span style="color:#f92672">:</span> <span style="color:#e6db74">'load-image'</span> });
}());
};
</code></pre></div><p>There are a couple of interesting things happening above, which can quickly
summarized as:</p>
<ul>
<li>Render the UI as the result of the <code>POST</code> request by performing a redirect.</li>
<li>Read the data that is submitted via the form via <code>event.request.formData()</code></li>
<li>Send the data to the open window (this will be the UI that we redirected the
user to in the first point).</li>
</ul>
<p>It is entirely up to you what you do with the data that has been posted to your
service worker, but in the case of my App I needed to show it directly in the UI
so I have to find the window the user is using and <code>postMessage</code> the data there.</p>
<p><em>index.html</em> - <a href="https://paul.kinlan.me/share/image/index.html">demo</a></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:#a6e22e">navigator</span>.<span style="color:#a6e22e">serviceWorker</span>.<span style="color:#a6e22e">onmessage</span> <span style="color:#f92672">=</span> (<span style="color:#a6e22e">event</span>) => {
<span style="color:#a6e22e">console</span>.<span style="color:#a6e22e">log</span>(<span style="color:#a6e22e">event</span>);
<span style="color:#a6e22e">imageBlob</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">event</span>.<span style="color:#a6e22e">data</span>.<span style="color:#a6e22e">file</span>;
<span style="color:#75715e">// Update the UI with the data that has been shared to it.
</span><span style="color:#75715e"></span> <span style="color:#a6e22e">imageShare</span>.<span style="color:#a6e22e">src</span> <span style="color:#f92672">=</span> <span style="color:#a6e22e">URL</span>.<span style="color:#a6e22e">createObjectURL</span>(<span style="color:#a6e22e">imageBlob</span>);
};
</code></pre></div><p>And that's about it. If you already have an API endpoint for your web forms,
then this is a simple, yet powerful addition that you can make to your site.</p>
<p>The Web Share Target API incredibly powerful platform primitive that breaks down
another barrier that web apps have had on their host platforms.</p>
This content originally appeared on Modern Web Development with Chrome and was authored by Paul Kinlan