title published description tags cover_image published_at
Handling files in enterprise web solutions
true
Using the File Handling API to create file use shortcuts for enterprise
Enterprise, Web, Fugu
2024-03-04 18:20 +0100

Correct file handling can be cumbersome without proper system integration and selection of which applications can handle what types of files.

Web solutions have previously been disconnected from this because of limitations in the browser to system integration.

This is no longer the case, and this post will cover the key parts of making a solution that works.

To make things more concrete, we will look at how to create a simple GeoJSON viewer application that will automatically open and display GeoJSON files when double-clicking them (or selecting the app on "Open with...").

Note: This feature is desktop only

File Handling in manifest.json

In the file manager and the desktop view of most operating systems, there is a file handling UI that directs the user to use a certain app to handle the chosen file. Previously, this was limited to native app installations, but now, it's possible for web applications to register handlers tied to PWAs that will handle files matching certain extension patterns.

Thomas Steiner has created a great article about this here:

https://developer.chrome.com/docs/capabilities/web-apis/file-handling

What's needed

  1. file_handlers entries in manifest.json
  2. Code to handle loading the files
  3. Installation of the web application

Let's look at these one by one.

file_handlers in manifest.json

Note: In order to make the web application 'installable', a manifest file needs to be present with minimal information as described here.

Inside the manifest file, create a section with the key file_handlers. This contains an array of the file types that the application can handle (accept), which URL to open (action, must be within the scope of the installed PWA), icons and launch_type, describing if new instances should be made for each file on "Open with..." selection.

...
"file_handlers": [
  {
    "action": "./?log=y",
    "accept": {
      "application/geo+json": [".geojson"]
    },
    "icons": [
      {
        "src": "./images/treasure-map-256.png",
        "sizes": "256x256",
        "type": "image/png"
      }
    ],
    "launch_type": "multiple-clients"
  }
]
...

In our case, we are interested in handling files with the .geojson extension and we will send it to our PWA with an additional query parameter to enable debug logging (log=y).

File loading handler(s)

When the PWA is opened by the OS via an "Open with..." action, the file(s) selected will be available through the launchQueue interface:

if ('launchQueue' in window && 'files' in LaunchParams.prototype) {
  launchQueue.setConsumer(async (launchParams) => {
    if (!launchParams.files.length) {
      return;
    }
    for (const fileHandle of launchParams.files) {
      // Handle the file.
      const file = await fileHandle.getFile();
      this.loadGeoJSONFile(file);
    }
  });
} else {
  console.log("File Handling API NOT supported");
}

Installation and usage

Open the application in a supported browser and click the install icon in the address bar.

Install PWA

Then go to the file explorer, right click a GeoJSON file (with the .geojson extension) and see how the freshly installed PWA is now registered to handle GeoJSON files.

Open with PWA

On first launch, the user is prompted to check if this should be the default behavior:

Default open with

When the PWA starts, the GeoJSON is loaded, passed to the map and immediately displayed:

GeoJSON loaded

Note: The collapsible activity log in the bottom of the screen is activated because of the additional log=y passed to the application.

Windows and Edge

The section above describes how the flow looks when using Chrome and Ubuntu. Here we will go through the same flow, but using Edge on Windows.

Credit: @kennethrohde

Installation: Edge Install PWA

Open .geojson file with the PWA: Edge Open With

Loading GeoJSON: Edge Loading GeoJSON

OpenLayers

In order to display the GeoJSON features on a map, we will use OpenLayers, which is a very powerful open-source mapping library that is also very simple to use.

In this post, we will not dive into all the details, so I will just show the snippets related to initializing the map and adding the GeoJSON data on top:

...
// Init map
this.#map = new ol.Map({
  layers: [
    new ol.layer.Tile({
      source: new ol.source.OSM(),
    }),
    this.#vectorLayer,
  ],
  target: this.#mapEl,
  view: new ol.View({
    center: [0, 0],
    zoom: 2,
  }),
});

...

// Load GeoJSON
plotGeoJSON(obj) {
  const features = new ol.format.GeoJSON({
    featureProjection: 'EPSG:3857'
  }).readFeatures(obj);

  const vectorSource = new ol.source.Vector({ features });

  this.#vectorLayer.setSource(vectorSource);

  setTimeout(() => {
    this.#map.getView().fit(vectorSource.getExtent(), { duration: 1000 });
  }, 500);
}

Creating GeoJSON files

Inside the project repository, there are a few GeoJSON samples generated with the awesome GeoJSON editor here: geojson.io. This web application let's you create features by drawing on a map and the features are then immediately reflected in a GeoJSON format on the right side of the screen.

Screenshot of geojson.io

TIL GitHub has a nice GeoJSON viewer built-in:

GitHub GeoJSON

Conclusion

Having the option of adding file handling on a system level is a very powerful feature and also extremely useful, e.g for enterprise tools to optimize workflows.

The effort involved in adding the feature in a PWA is close to nothing and personally I enjoyed experimenting with this very much.

The code is available here: https://github.com/larsgk/filehandling-geojson

The application is hosted here: https://larsgk.github.io/filehandling-geojson

Enjoy ;)