Capacitor writeFile - Saving pdf file is in invalid format

I am generating a pdf using pdfMake and writing it to an android device so that I can open it.
I am using Capacitor and the file system command does not appear to take a blob (this worked in cordova) so I am sending it a base64.
It seems to write ok but when I open the pdf it says the format is invalid.

Note: The cordova file plugin does not work with Capacitor (Cordova plugins not working with Capacitor) so it seems I am stuck with Capacitor file system commands.

Can I write to blob in capacitor or is there a problem with my code??

Help please, I have spend days on this!

   this.pdfObj = pdfMake.createPdf(docDefinition);

    if (this.plt.is('cordova')) {
      this.pdfObj.getBase64((data) => {
        this.pdfBase64 = data;
        console.log(this.pdfBase64);
      });
    }
  }

  downloadPdf() {
    const { Filesystem } = Plugins;

    if (this.plt.is('cordova')) {
      console.log('3');
        // Save the PDF to the data Directory of our App
        const fileName = 'defectreport.pdf';
        try {
          Filesystem.writeFile({
            path: fileName,
            data: this.pdfBase64,
            directory: FilesystemDirectory.Data,
            encoding: FilesystemEncoding.UTF8
          }).then((writeFileResult) => {
            console.log('File Written');
            Filesystem.getUri({
                directory: FilesystemDirectory.Data,
                path: fileName
            }).then((getUriResult) => {
                console.log(getUriResult);
                const path = getUriResult.uri;
                this.fileOpener.open(path, 'application/pdf')
                .then(() => console.log('File is opened'))
                .catch(error => console.log('Error openening file', error));
            }, (error) => {
                console.log(error);
            });
          });
          console.log('writeFile complete');
        } catch (error) {
          console.error('Unable to write file', error);
        }
1 Like

Oh and if I convert the base64 string manually to a file using a web tool it works fine as a pdf.

Did you double check that your this.pdfBase64 contains the data before writing? Just to be sure you know

Then, did you get any exception? whatā€™s the stacktrace of the error? or no error?

Finally, have you try to converting the base64 to a blob to pass it to the write function?

  1. Yes I write the data to the console from the variable to check it. I have used it from here to manually convert to a pdf fine. It does not have a mime/type prefix in the string but otherwise works fine.

  2. No exception or warning. The only issue is an error from the google pdf app saying the file is invalid when it tries to open.

  3. This is my issue. Capacitor does not take blobs. In cordova I was using a blob (worked fine) but now I have to use base64. Capacitor writeFile only takes a string for the data :frowning:

Maybe thatā€™s the problem?

Except that, if you print out to the console the value of writeFileResult, whatā€™s the result?

Also do you get anything as result for getUriResult or just empty?
If not empty, the value of getUriResult.uri seems ok?
If yes, if you donā€™t use fileOpener but for example readFile to get the content of the saved file, do you get empty or do you get data?

Yeah I was thinking about manually adding a prefix to see if that worked. I will give it a try, Hopefully base64 is the format I am meant to use, there is no detailed documentation I can find.
https://capacitor.ionicframework.com/docs/apis/filesystem

writeFileResult returns an empty object: ā€œ{ }ā€

getUriResult gets the file path and name and is used to successfully open the file. Value: ā€œfile:///data/user/0/io.ionic.starter/files/defectreport.pdfā€

1 Like

let me know if that work with adding the the prefix, Iā€™m curious

where you browse the files of your phone, do you find that file file:///data/user/0/io.ionic.starter/files/defectreport.pdf? I mean not using code I mean really while using the file explorer

I will try that tomorrow too. To be honest I donā€™t even know how to find that file path on an android phone manually. I tried and failed. My personal phone is an iphone haha.

1 Like

Well putting the MIME type prefix on didnā€™t help.
It works fine (without prefix) if I cut and paste the string and covert to pdf file using this:
http://base64converter.com/

It turns out that it is just writing it as a text file. I should have checked for that ages ago. I guess this means the capacitor file system canā€™t write a pdf with a base64 string.
Or maybe I need to change the encoding from UTF8 to something else?

I donā€™t think so, didnā€™t checked deeply but seems that Capacitor ā€œjustā€ create a File from what you provide, so if you provide a pdf path it should create a pdf file (see https://github.com/ionic-team/capacitor/blob/781d2307fe92f535c1ec2a692ac6c0f1dcbe924f/android/capacitor/src/main/java/com/getcapacitor/plugin/Filesystem.java#L212)

Whatā€™s the value of the path you are providing to Capacitor writeFile? Does it contains the extension ā€œ.pdfā€?

2 Likes

You sir are a genius! I would buy you a beer if you were in my timezone :wink: Great idea to look at the code.
I got it working.
I just needed to remove the UTF8 encoding.

Soā€¦
If you look at the docs here (https://capacitor.ionicframework.com/docs/apis/filesystem/#type-401)
It says that the encoding defaults to UTF8, so I didnā€™t think removing it would make any difference.

However looking at the code in the link you sent it defaults to plain text if you provide a value in encoding.

Charset charset = this.getEncoding(encoding);
...
 // if charset is not null assume its a plain text file the user wants to save
3 Likes

Awesome, glad to hear you found the solution!

Thatā€™s well noted, you never know, I might travel one of these days :wink:

Happy coding, have a great Sunday

Hello,
I still have the same problem.
After saving a pdf and opening the same it displays the invalid formed message to the PDF

This is my code for creating the PDF file:
`const { Filesystem } = Plugins;

console.log("compartilhar: ", Filesystem);
try {
  Filesystem.writeFile({
    path: 'wsp.pdf',
    data: "PDF capacitor test",
    directory: FilesystemDirectory.Documents
  }).then((res) => {
    console.log("success pdf: ", res);
  });
} catch(e) {
 console.error('Unable to write file: ', e);
}`

Can someone help me?
Thanks in advance!

If you are using the below (and it is not just a placeholder) you are just sending text as data. Not a pdf.

  1. If you donā€™t specify an encoding type it assumes it is a base64 string.
  2. You are sending raw text. Not a base64 string. Are you using a pdf generator like pdfMake?

See my code below:

downloadPdf(pdfBase64: string) {
    const { Filesystem } = Plugins;

    if (this.plt.is('cordova')) {
        // Save the PDF to the device
        const fileName = 'defectreport.pdf';
        try {
          Filesystem.writeFile({
            path: fileName,
            data: pdfBase64,
            directory: FilesystemDirectory.Documents
            // encoding: FilesystemEncoding.UTF8
          }).then((writeFileResult) => {
            Filesystem.getUri({
                directory: FilesystemDirectory.Documents,
                path: fileName
            }).then((getUriResult) => {
                const path = getUriResult.uri;
                this.fileOpener.open(path, 'application/pdf')
                .then(() => console.log('File is opened'))
                .catch(error => console.log('Error openening file', error));
            }, (error) => {
                console.log(error);
            });
          });
        } catch (error) {
          console.error('Unable to write file', error);
        }
      } else {
      // On a browser simply use download
      this.defectReportService.getPdfObj().download();
    }
  }
1 Like

Hello @wekas
Iā€™m trying to create a file with PDF format.
Regardless of the content of the PDF being a base64 or simply a text I would have to be able to open the same one, however I can not open any PDF file

This is my complete code:

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

import { FileOpener } from '@ionic-native/file-opener/ngx';

async createFile(base64: string) {
    const { Filesystem } = Plugins;
    try {
      const fileName = "wsp.pdf";
      Filesystem.writeFile({
        path: fileName,
        data: base64,
        directory: FilesystemDirectory.Documents
      }).then((res) => {
        console.log("Success create file: ", res);
        Filesystem.getUri({
            directory: FilesystemDirectory.Documents,
            path: fileName
        }).then((getUriResult) => {
            const path = getUriResult.uri;
            this.fileOpener.open(path, 'application/pdf')
            .then(() => console.log('Success open'))
            .catch(error => console.log('Error open File: ', error));
        }, (error) => {
            console.log(error);
        });
      });
    } catch(e) {
     console.error('Error create File: ', e);
    }
  }

Thanks for listening!

Hi Luca,

I know its not what you want but if you use this it should work for text:

Filesystem.writeFile({
            path: 'doc.txt',
            data: 'I am a line of text',
            directory: FilesystemDirectory.Documents
            encoding: FilesystemEncoding.UTF8  //Any item here assumes it is text
          })

As for the pdf here is what I would try:

  1. MIME type:
    Do you have a MIME type on your Base64 data? You may need it for the pdf app on the device to recognise it.
    I add it in my code:
this.myService.getPdfObj().getBase64((data) => {
        const pdfBase64 = 'data:application/pdf;base64,' + data;
        this.downloadPdf(pdfBase64);
      });
  1. Try writing you base64 string to the console and copy and paste it into an online pdf generator like this:
    http://base64converter.com/

  2. Find the file on your device and email to computer then open it in a browser (different app). If it doesnā€™t work try changing the extension to .txt to make sure it is not text.

Let me know if any of that works.

Les adjunto mi soluciĆ³n
import { Plugins, FilesystemDirectory } from ā€˜@capacitor/coreā€™;
const { Filesystem } = Plugins;

let base64Code = pdfAsString;
try {
const result = await Filesystem.writeFile({
path: ā€˜carnet.pdfā€™,
data: base64Code,
directory: FilesystemDirectory.Documents,
})
console.log(ā€˜Wrote fileā€™, result);
} catch(e) {
console.error(ā€˜Unable to write fileā€™, e);
}

This is my solution with capacitor v3:

    downloadFile(urlFile: string): void {
        this.http.get(urlFile, {
            responseType: 'blob',
            reportProgress: true,
            observe: 'events'
        }).subscribe(async (event) => {
            if (event.type === HttpEventType.DownloadProgress) {
                this.downloadProgress = Math.round((100 * event.loaded) / event.total);
            } else if (event.type === HttpEventType.Response) {
                this.downloadProgress = 0;
                const name = urlFile.substr(urlFile.lastIndexOf('/') + 1);
                const base64 = await this.convertBlodToBase64(event.body) as string;
                const savedFile = await Filesystem.writeFile({
                    path: name,
                    data: base64,
                    directory: Directory.Documents,
                });
            }
        });
    }
convertBlodToBase64 = (blob: Blob) => new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onerror = reject;
        reader.onload = () => {
            console.log('convBase64', reader.result);
            resolve(reader.result);
        };
        reader.readAsDataURL(blob);
    });

The solution for the FILE_NOTCREATED error, you should add to the <application> in AndroidManifest:

android:requestLegacyExternalStorage="true"

The solution for the error: package android.support.v4.content does not exist, try this:

//replace
public class FileProvider extends android.support.v4.content.FileProvider
//for this
public class FileProvide extends androidx.core.content.FileProvider

Man, you saved my day. Spent so much time analyzing where it was going wrong.Though this does seem unexpected behavior. There should be better documentation.