Promise reject on getting formData in WebshareTarget Handler

225 views Asked by At

i constantly getting a Promise reject when i want to handle the POST Request in the service worker of my application. I like to send a file to my pwa and as of the documentation the POST request should have a form body which i can read with .formData() but the this method returns rejected promise everytime. Any ideas what i do wrong?

My Manifest:

...
"share_target": {
    "action": "/editor/image",
    "method": "POST",
    "enctype": "multipart/form-data",
    "params": {
      "title": "name",
      "text": "description",
      "url": "link",
      "files": [
        {
          "name": "file",
          "accept": [
            "*/*"
          ]
        }
      ]
    }
  },
...

My service worker:

importScripts("./ngsw-worker.js");

self.addEventListener("fetch", (event) => {
  const url = new URL(event.request.url);

  if (url.origin === location.origin && url.pathname === "/editor/image" && event.request.method === "POST") {
    console.log("Retrieve");
    handleFileShare(event);
  }
});

function handleFileShare(event) {
  let dataPromise;
  try {
    dataPromise = event.request.formData(); // <- FAIL HERE
  } catch (error) {
    console.error(error);
  }

  event.respondWith(Response.redirect("/editor"));
  event.waitUntil(
    (async function () {
      try {
        const client = await self.clients.get(event.resultingClientId);
        const data = await dataPromise;
        const file = data.get("file");
        client.postMessage({ file, action: "load-image" });
      } catch (error) {
        console.log(error);
      }
    })()
  );
}
1

There are 1 answers

0
DenverCoder9 On

Your Web Application Manifest looks good, but for the service worker you need to structure the code differently. Here is an MVP implementation that shows the required steps:

// Service Worker
self.addEventListener('fetch', (fetchEvent) => {
  /* Start Web Share Target */
  if ((fetchEvent.request.url.endsWith('/editor/image')) &&
      (fetchEvent.request.method === 'POST')) {
    // Call respondWith() early on, so the service worker stays alive.
    return fetchEvent.respondWith((async () => {
      // This function is 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));
      // You need to redirect the user somewhere, since the path
      // /editor/image does not actually exist.
      return Response.redirect('./?share-target', 303);
    })());
  }
  /* End Web Share Target */

  /* ... */
});

Then on the client you need to detect the situation where the user was redirected:

const restoreImageFromShare = async () => {
  const mediaCache = await getMediaCache();
  const image = await mediaCache.match('shared-image');
  if (image) {
    const blob = await image.blob();
    // Do something with the image and housekeep the cache.
    await mediaCache.delete('shared-image');
  }
};

// Check for the URL parameter that the service worker sets.
if (location.search.includes('share-target')) {
  restoreImageFromShare();
} else {
  drawDefaultImage();
}