How the image editing app Photopea uses the File Handling API to let users open files from their file explorer

The File Handling API allows web applications to register themselves as a file handler for file formats the application can support. Learn how the image editing application Photopea makes use of this API.

Introduction

(This article is also available in form of a video.)

Photopea is a free online image editor developed by Ivan Kutskir. Ivan started working on the app in 2012, and maintains a blog sharing the major features he adds to Photopea. Photopea can work with PSD (Adobe Photoshop), XCF (GIMP), Sketch (Sketch App), XD (Adobe XD), and CDR (CorelDRAW) formats.

The Photopea app.

File handling in Photopea

As an installable PWA, Photopea runs in a standalone window when the user chooses to install the app. Doing so unlocks a PWA super power, which Photopea makes heavy use of: file handling.

The declarative part of the File Handling API

After installation, Photopea registers itself as a file handler with the operating system for the different file formats it supports. This happens in the Web App Manifest, by adding the file_handlers field. Each supported file type is an object, the action has a relative URL as its value, the accept object a map of MIME types and associated file extensions. For example, {"image/jpeg": [".jpeg", ".jpg"]}. The following code is the production Web App Manifest of Photopea, with the relevant parts highlighted.

{
    "name": "Photopea",
    "short_name": "Photopea",
    "display": "standalone",
    "icons": [
        {  "src": "promo/icon512.png",     "type": "image/png", "sizes": "512x512"  },
        {  "src": "promo/maskable512.png", "type": "image/png", "sizes": "512x512", "purpose":"maskable"  }
    ],
    "start_url": "/?utm_source=homescreen",
    "background_color":"#0f171d",
    "theme_color": "#474747",
    "file_handlers": [
        { "action": "/", "accept": {  "image/psd" : [ ".psd" ]  } },
        { "action": "/", "accept": {  "image/jpeg": [ ".jpeg", ".jpg" ]  } },
        { "action": "/", "accept": {  "image/png" : [ ".png" ]  } },
        { "action": "/", "accept": {  "image/webp": [ ".webp" ]  } },
        { "action": "/", "accept": {  "image/bmp" : [ ".bmp" ]  } },
        { "action": "/", "accept": {  "image/gif" : [ ".gif" ]  } },
        { "action": "/", "accept": {  "image/svg+xml": [ ".svg" ]  } },
        { "action": "/", "accept": {  "image/pdf" : [ ".pdf" ]  } },
        { "action": "/", "accept": {  "image/tiff": [ ".tif", ".tiff" ]  } },
        { "action": "/", "accept": {  "image/ai"  : [ ".ai"  ]  } },
        { "action": "/", "accept": {  "image/psb": [ ".psb" ]  } },
        { "action": "/", "accept": {  "image/xcf": [ ".xcf" ]  } },
        { "action": "/", "accept": {  "image/sketch": [ ".sketch" ]  } },
        { "action": "/", "accept": {  "image/xd" : [ ".xd"  ]  } },
        { "action": "/", "accept": {  "image/pxd": [ ".pxd" ]  } },
        { "action": "/", "accept": {  "image/cdr": [ ".cdr" ]  } },
        { "action": "/", "accept": {  "image/eps": [ ".eps", ".ps" ]  } },
        { "action": "/", "accept": {  "image/x-icon": [ ".ico" ]  } },
        { "action": "/", "accept": {  "image/jpx": [ ".jpx" ]  } },
        { "action": "/", "accept": {  "image/jp2": [ ".jp2" ]  } },
        { "action": "/", "accept": {  "image/x-tga": [ ".tga" ]  } },
        { "action": "/", "accept": {  "image/vnd-ms.dds": [ ".dds" ]  } }
    ],
    "share_target": {
        "action": "/",
        "method": "POST",
        "enctype": "multipart/form-data",
        "params": {
            "files": [
            {
                "name": "image",
                "accept": ["image/jpeg", "image/png", "image/webp", "image/gif"]
            }
          ]
        }
    }
}

The macOS Finder with the user right-clicking a file and then choosing 'Open with' Photopea.

The imperative part of the File Handling API

The imperative part of the API then deals with actually handling the file(s) that the operating system passes to the PWA. Photopea's code is obviously heavily minimized and uglified, but nevertheless the gist of the snippet below is not so hard to grasp. The LaunchQueue interface (minified as N) has a setConsumer() method, which accepts a function as an argument. This function in turn takes a LaunchParams object (minified as W) . This LaunchParams object has a files property pointing at a read-only array of FileSystemHandle objects, which the rest of the code then loops over and for each obtains the File object (minified as G) by calling getFile(). This file is then passed off to other logic in Photopea that takes care of displaying the file.

var N = window.launchQueue;
if (N) {
  var $ = this.UA;
  N.setConsumer(function (W) {
    var O = W.files;
    console.log(O);
    for (var Y = 0; Y < O.length; Y++) {
      var T = O[Y];
      T.getFile().then(function (G) {
        $.YO([G], null, null, null, [T]);
      });
    }
  });
}

Conclusions

Users have been asking for Photopea to become a file handler for images for a long time. In 2020, when the question appeared, this feature was completely unthinkable, but an eager user discovered the file handling API in its earliest stages in the beginning of 2022 when it was still behind a flag. File handling eventually shipped in Chrome 102 and has been a beloved Photopea feature used on a daily basis by its users, some even calling it a gamechanger. Be sure to give Photopea a try, install it on your desktop, and then try opening one of the file formats it supports! Happy image editing!