How can I save a downloaded file with Capacitor in Ionic4 Angular?

Viewed 15031

I have recently published my Ionic 4 Angular App both as web app and as native Android app.
In the native Android app, everything works fine, except saving a downloaded file.

To download and save a file, I've always used file-saver npm package as follows (this is a shared service that I invoke every time I have to download something, from a PDF to an image, etc...):

import { saveAs } from 'file-saver';

// ...

saveGenericFile(api: string, fileinfos: any, idFile: string): any {
    let mediaType = 'application/pdf';
    let fileName = '';

    if (fileinfos != null) {
      mediaType = fileinfos.contentType;
      fileName = fileinfos.fileName;
    }

    const headers = this.base.commonHeader;
    const url = this.baseUrl + api + '?id=' + idFile;
    this.http.post(url, null, { headers, responseType: 'blob' }).subscribe(
      (response) => {
        // tslint:disable-next-line: prefer-const
        let blob = new Blob([response], { type: mediaType });
        saveAs(blob, fileName);
      }, e => {
        console.error(e);
        this.toastsvc.generateToast('ERROR! An error occurred while saving this File, try later or contact support', 'danger');
      }, () => {
        /* do nothing */
      }
    );
  }

As I said above, this code snippet works fine, but just when I have to save something from the web release.

The only online examples I was able to find were all about Cordova and/or previous/deprecated versions.
About Capacitor, I just found this documentation and from that I just found this code snippet:

import { Plugins, FilesystemDirectory, FilesystemEncoding } from '@capacitor/core';

const { Filesystem } = Plugins;

fileWrite() {
  try {
    Filesystem.writeFile({
      path: 'secrets/text.txt',
      data: "This is a test",
      directory: FilesystemDirectory.Documents,
      encoding: FilesystemEncoding.UTF8
    });
  } catch(e) {
    console.error('Unable to write file', e);
  }
}

But the issue is that my function above returns a blob and this one only accept a string for data.

So, is there any Capacitor-Native equivalent feature that I can use to download (and save) Blob files both when running as web-app and when running as Android native app?


UPDATE

I've also tried the following, but it's not working:

saveGenericFile(api: string, fileinfos: any, gidFile: string): any {
    let mediaType = 'application/pdf';
    let fileName = '';

    if (fileinfos != null) {
      mediaType = fileinfos.contentType;
      fileName = fileinfos.fileName;
    }

    const headers = this.base.commonHeader;
    const url = this.baseUrl + api + '?id=' + gidFile;
    this.http.post(url, null, { headers, responseType: 'blob' }).subscribe(
      (response) => {
        if (!this.useCordovaDl) {
          // tslint:disable-next-line: prefer-const
          let blob = new Blob([response], { type: mediaType });
          saveAs(blob, fileName);
        } else {
          this.blobFileWrite(fileName, response);
        }
      }, e => {
        console.error(e);
        this.toastsvc.generateToast('ERROR! An error occurred while saving this File, try later or contact support', 'danger');
      }, () => {
        /* do nothing */
      }
    );
  }

  blobFileWrite(filename: string, blobfile: Blob) {
    const reader = new FileReader();

    // This fires after the blob has been read/loaded.
    reader.addEventListener('loadend', (e: any) => {
      const text = e.srcElement.result;
      this.fileWrite(filename, text);
    });

    // Start reading the blob as text.
    reader.readAsText(blobfile);
  }

  fileWrite(filename: string, filedata: string) {
    try {
      Filesystem.writeFile({
        path: filename,
        data: filedata
        // ,
        // directory: FilesystemDirectory.Documents,
        // encoding: FilesystemEncoding.UTF8
      });
    } catch (e) {
      console.error('Unable to write file', e);
    }
  }

UPDATE #2

It looks like there's still an opened issue on GitHub about saving Blob data with Capacitor. I will look for a Cordova solution in the meanwhile. Or, I will simply disable every download button if platform is android or ios.
I will post every working Cordova Workaround down here, if I'll be able to find one

3 Answers

if what you want to achieve is save the file in the filesystem, you can save it with string base64 to filesystem

image = "data:image/png;base64,iVBORw0KGgo... bla bla bla"
Capacitor.Plugins.Filesystem.appendFile({
    data:image,
    path:"directory/image.png",
    directory:"DOCUMENTS"
}).then(c=>{console.log("se escribiooo")})

its result in image in your filesystem (i just tried this code in the android emulator), just use the method appendFile instead writeFile, and base64 instead blob, if you only have the blob you just have to convert it to base64

Sharing my workaround with you.

I'm using web-implementation of the Filesystem plugin. Don't know if it's a good solution but it helps solving my problem.

import { FilesystemDirectory, FilesystemEncoding } from '@capacitor/core'
// const { Filesystem } = Plugins // => Doesn't work with blob in native
// Workaround: Use Web-Implementation of plugin
import { FilesystemPluginWeb } from '@capacitor/core/dist/esm/web/filesystem.js'
const Filesystem = new FilesystemPluginWeb()

You can either create a plugin, to which you can pass a URL and it will download natively. As you have already discovered passing blob/file is not possible, yet.

If there's no plugin available or you don't want to create one. You can invoke the native function from js which you can listen for in native side for URL etc.

Related