Skip to content

[Angular | Capacitor] How to save and/or open file

In this short tutorial, I will show you how to save and open files in a capacitor app.

Georg Höller
Georg Höller
3 min read
[Angular | Capacitor] How to save and/or open file
Photo by Jonah Pettrich / Unsplash

The Capacitor Framework helps you in a lot of situations and makes it easy to build an app out of a finished angular website.
But not everything is working out of the box. In this short tutorial, I will show you how to save and open files in a capacitor app.

Version Information

Capacitor: 3.2
Angular: 11.1

Capacitor Utils

To prevent code duplications I created a 'capacitorUtils.ts' file in my angular workspace and created the class 'CapacitorUtils'. Place the file in a folder you can access from everywhere - I placed it in a shared folder.
Your file should look like the following:

import { Capacitor } from '@capacitor/core';

export class CapacitorUtils {
    static readonly isApp = Capacitor.getPlatform() !== 'web';
}

I added a static variable to check the platform we are running at. You may need this to differ if you should run the following code or the default browser download logic.

Plugins

For this tutorial you have to install 3 Plugins:

npm install @capacitor/filesystem
npm install cordova-plugin-file-opener2
npm install @ionic-native/file-opener

References:
https://capacitorjs.com/docs/apis/filesystem
https://ionicframework.com/docs/native/file-opener

With the filesystem plugin from capacitor, you can write data to the phone's file system.
With the ionic file opener, you can open the file with the default app for the specific mime type.

Don't forget to call capacitor to sync!

npx cap sync

Android

If you don't use android - skip this section :)

After the sync command I wasn't able tobuild the android solution.
It failed at the file "FileProvider" because the plugin is too old.
To fix this open the file and find the following line:

public class FileProvider extends android.support.v4.content.FileProvider {

Replace the whole line with this:

public class FileProvider extends androidx.core.content.FileProvider {

Edit 09.02.22
You can fix this problem with jetifier - just run the following (works with every old plugin which doesn't support androidx):

npm install jetifier
npx jetify
npx cap sync android

Save and Open Blob Data

Open your 'capacitorUtils.ts' file and add the following method in the CapacitorUtils class:

static async writeAndOpenFile(data: Blob, fileName: string) {
    var reader = new FileReader();
    reader.readAsDataURL(data);
    reader.onloadend = async function () {
        var base64data = reader.result;
        try {
            const result = await Filesystem.writeFile({
                path: fileName,
                data: <string>base64data,
                directory: Directory.Data,
                recursive: true
            });
            let fileOpener: FileOpener = new FileOpener();
            fileOpener.open(result.uri, data.type)
                .then(() => console.log('File is opened'))
                .catch(e => console.log('Error opening file', e));

            console.log('Wrote file', result.uri);
        } catch (e) {
            console.error('Unable to write file', e);
        }
    }
}

Don't forget to add all the imports for the source above:

import { FileOpener } from '@ionic-native/file-opener/ngx';
import { Filesystem, Directory } from '@capacitor/filesystem';

This code takes a blob object and writes it to the data directory from the phone. To open the file we created a new FileOpener and use the resulting path from 'writeFile'.
If everything works as intended we should have downloaded the file to our device and it should open automatically.

Now you can call this code from anywhere in your angular app.

if (CapacitorUtils.isApp)
    CapacitorUtils.writeAndOpenFile(result.data, filename);
else
    saveAs(result.data, filename);

Problems

Whenever I run into any kind of issue with this code I will add it here:

  1. Recently I had to download and open a .csv file and it failed. I had to set the mime-type manually to 'text/plain'.

I hope you enjoyed reading this short capacitor tutorial. Keep coding!

Angular

Comments


Related Posts

Members Public

[Angular | Storybook] Tailwind, Directives, Content Projection, Icons and i18n

Install packages npm i -D @nx/storybook npm i -D @storybook/angular Create config nx g @nx/storybook:configuration <project-name> I created the config inside the main app to share some configs/modules. My stories are located at libs/shared/ui. That's why I had to

[Angular | Storybook] Tailwind, Directives, Content Projection, Icons and i18n
Members Public

[Angular | RxJS] BehaviorSubject with custom states

Whenever I call an api, often my frontend has to go through some different visualization steps. Imagine you have some kind of search input and a list which displays the results but you want to show an loading indicator, an error hint and something different when the result is empty.

[Angular | RxJS] BehaviorSubject with custom states
Members Public

[Angular] Dynamic App Config and Translations in a Submodule

When I'm setting up a new angular application, I always start with translation support and an external app config json loader. You should never start a new app without an internationalization (i18n) system - even if you only support one language! We are going to use the package

[Angular] Dynamic App Config and Translations in a Submodule