File Web Share Target

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 data out of their silo and into other web sites and apps; they also need to be able to receive the data from other native apps and sites.


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


Print Share Comment Cite Upload Translate Updates
APA

Paul Kinlan | Sciencx (2019-02-15T15:52:03+00:00) File Web Share Target. Retrieved from https://www.scien.cx/2019/02/15/file-web-share-target/

MLA
" » File Web Share Target." Paul Kinlan | Sciencx - Friday February 15, 2019, https://www.scien.cx/2019/02/15/file-web-share-target/
HARVARD
Paul Kinlan | Sciencx Friday February 15, 2019 » File Web Share Target., viewed ,<https://www.scien.cx/2019/02/15/file-web-share-target/>
VANCOUVER
Paul Kinlan | Sciencx - » File Web Share Target. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2019/02/15/file-web-share-target/
CHICAGO
" » File Web Share Target." Paul Kinlan | Sciencx - Accessed . https://www.scien.cx/2019/02/15/file-web-share-target/
IEEE
" » File Web Share Target." Paul Kinlan | Sciencx [Online]. Available: https://www.scien.cx/2019/02/15/file-web-share-target/. [Accessed: ]
rf:citation
» File Web Share Target | Paul Kinlan | Sciencx | https://www.scien.cx/2019/02/15/file-web-share-target/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.