![Palances Liao](https://web.dev/images/authors/pliao.jpg) Palances Liao

<br />

## The modern way

### Using the Web Share Target API

First, declare a `share_target` in your web app manifest that lists an `action` (your URL that handles
shared files), a `method` (`"POST"` for files) and an `enctype` (`"multipart/form-data"` for
files) and a `params` object that contains a `files` property with an array of objects with a `name`
and `accept` property that list the sharable file types and the name to obtain them.

    {
      "share_target": {
        "action": "/receive-files/",
        "method": "POST",
        "enctype": "multipart/form-data",
        "params": {
          "files": [
            {
              "name": "image",
              "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
            }
          ]
        }
      }
    }

You then need to handle incoming `POST` requests in the service worker. The file is temporarily
stored in a media cache, so it can be consumed in the client. This can be done by redirecting the
app to a URL with a special marker query parameter like `share-target`.

    self.addEventListener('fetch', (fetchEvent) => {
      if (fetchEvent.request.url.endsWith('/receive-files/') && fetchEvent.request.method === 'POST') {
        return fetchEvent.respondWith(
          (async () => {
            const formData = await fetchEvent.request.formData();
            const image = formData.get('image');
            const keys = await caches.keys();
            const mediaCache = await caches.open(keys.filter((key) => key.startsWith('media'))[0]);
            await mediaCache.put('shared-image', new Response(image));
            return Response.redirect('./?share-target', 303);
          })(),
        );
      }
    });

Finally, you need to consume the file in the client.

    window.addEventListener('load', async () => {
      if (location.search.includes('share-target')) {
        const keys = await caches.keys();
        const mediaCache = await caches.open(
          keys.filter((key) => key.startsWith('media'))[0],
        );
        const image = await mediaCache.match('shared-image');
        if (image) {
          const blob = await image.blob();
          await mediaCache.delete('shared-image');
          // Handle the shared file somehow.
        }
      }
    });


## Further reading

- [Receiving shared data with the Web Share Target API](https://web.dev/web-share-target)
- [W3C Web Share Target API](https://w3c.github.io/web-share-target/)

## Demo

<iframe src="https:///frame/articles/files/receive-shared-files/index_911f26d13f126c497073cd0520ed17d14d96871a9316a1b2ab48c567c1aaff96.frame" class="framebox inherit-locale " allow="clipboard-write https://" allowfullscreen is-upgraded></iframe>

### HTML

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <link rel="manifest" href="manifest.json" />
        <title>How to receive shared files</title>
        <link rel="stylesheet" href="style.css" />
        <!-- TODO: Devsite - Removed inline handlers -->
        <!-- <script>
          if ('serviceWorker' in navigator) {
            window.addEventListener('load', async () => {
              const registration = await navigator.serviceWorker.register(
                'sw.js',
              );
              console.log(
                'Service worker registered for scope',
                registration.scope,
              );
            });
          }
        </script>
        <script src="script.js" type="module"></script> -->
      </head>
      <body>
        <h1>How to receive shared files</h1>
        <p>Install the app. After the installation, try sharing an image to it from another app.
      </body>
    </html>

### CSS


            html {
      box-sizing: border-box;
      font-family: system-ui, sans-serif;
      color-scheme: dark light;
    }

    *, *:before, *:after {
      box-sizing: inherit;
    }

    body {
      margin: 1rem;
    }

    img {
      height: auto;
      max-width: 100%;
      display: block;
    }
            
### JS


            window.addEventListener('load', async () => {
      if (location.search.includes('share-target')) {
        const keys = await caches.keys();
        const mediaCache = await caches.open(
          keys.filter((key) => key.startsWith('media'))[0],
        );
        const image = await mediaCache.match('shared-image');
        if (image) {
          const blob = await image.blob();
          await mediaCache.delete('shared-image');
          // Handle the shared file somehow.
        }
      }
    });