Open PDF from Base64 string

Hi everyone, I’m trying to open a PDF document, starting from a base64 string.

I’ve tried everything, but I need a little support to solve this problem.

I use File and FileOpener

import { File } from '@ionic-native/file/ngx';
import { FileOpener } from '@ionic-native/file-opener/ngx';
  1. This simple HREF, open the PDF in iOS device, I don’t know why doesn’t works in Android device.
    The PDF is opened over all and it is not possible to close it unless killing the app.
<ion-item href="data:application/pdf;base64,{{ stringBase64PDF }}">
  <ion-label>HREF</ion-label>
</ion-item>  
  1. I try to follow the example in this topic Ionic - Base64 PDF
openPDF () {
    
    fetch('data:application/pdf;base64,' + this. stringBase64PDF, {
        method: "GET"
    })
    .then(res => res.blob()).then(blob => {
      console.log("created blob");
      this.file.createFile(this.file.dataDirectory, 'temp.pdf', true)
      .then(() => {
        console.log("file created");
        this.file.writeFile(this.file.dataDirectory, 'temp.pdf', blob, { replace: true })
        .then(res => {
          console.log("file writed");
          this.opener.open(res.toInternalURL(), 'application/pdf')
          .then((res) => {
            console.log('file opened')
          }).catch(err => {
            console.log('open error')
          });
        }).catch(err => {
          console.log('write error')     
        });
      }).catch(() => {
        console.log("create error");
      })
      
    }).catch(err => {
      console.log('blob error')
    });
  }

the logs arrive up to “file created”.

  1. I also tried to use a conversion function
b64toBlob(b64Data, contentType = '', sliceSize = 512) {
    
    var byteCharacters = atob(b64Data);
    var byteArrays = [];

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
      var slice = byteCharacters.slice(offset, offset + sliceSize);

      var byteNumbers = new Array(slice.length);
      for (var i = 0; i < slice.length; i++) {
        byteNumbers[i] = slice.charCodeAt(i);
      }

      var byteArray = new Uint8Array(byteNumbers);

      byteArrays.push(byteArray);
    }

    var blob = new Blob(byteArrays, { type: contentType });
    return blob;

  }

I noticed that passing the string in base64 directly as content, the fileopener opens but with an invalid file format, while trying to convert it to blob, it stops creating the file and does not write it.

savebase64AsPDF(folderpath, filename, content, contentType, b642blob = true) {
    // Convert the base64 string in a Blob
    var DataBlob = (b642blob) ? this.b64toBlob(content, contentType) : content;
    // content = "data:application/pdf;base64,"+ content;
    console.log("Starting to write the file");

    
    this.file.checkFile(folderpath + 'argos', filename)
    .then((succ)=> {
      console.log("checkFile then", succ);
      this.file.writeFile(folderpath + 'argos', filename, DataBlob, { replace: true })
      .then(res => {
        console.log("writeFile then", res);
        // this.opener.open(res.toInternalURL(), contentType)
        this.opener.open(folderpath + 'argos/' + filename, contentType)
        .then((res) => {
          console.log("open then", res);
        }).catch((err) => {
          console.log("open catch", err);
        });
      }).catch((err) => {
        console.log("writeFile catch", err);
      });
    }).catch((err) => {
      console.log("checkFile error", err)
      this.file.createFile(folderpath + "argos", filename, true)
      .then((succ) => {
        console.log("createFile then", succ);
        this.file.writeFile(folderpath + 'argos', filename, DataBlob, { replace: true })
        .then(res => {
          console.log("writeFile then", res);
          // this.opener.open(res.toInternalURL(), contentType)
          this.opener.open(folderpath + 'argos/' + filename, contentType)
          .then((res) => {
            console.log("open then", res);
          }).catch(err => {
            console.log("open catch", err)
          });
        }).catch(err => {
          console.log("writeFile catch", err)
        });
      }).catch((err) => {
        console.log("createFile then", err);
      })
    });

  }

I have tried to convert your base64 data to binary using javascript’s atob() function and then wrote it to the temp file. See if this works.

Make an instance variable like below in your constructor:

this.string_Base64PDF  = stringBase64PDF;

and then somewhere along the lines of creating a temp file

this.file.createFile(this.file.dataDirectory, 'temp.pdf', true)
      .then(() => {
        console.log("file created");
        this.file.writeFile(this.file.dataDirectory, 'temp.pdf', atob(this.string_Base64PDF) , { replace: true })

Also, note that for a PDF file to open, the android device needs to have a PDF reader too.

Thanks vivek-23, using your advice atob(stringBase64PDF) the viewer opens, shows the 4 pages of the PDF… but they are all white (empty), what can it depend on?

In Xcode console output:

To Native Cordova ->  File write File1046711781 ["options": [cdvfile://localhost/library-nosync/temp.pdf, %PDF-1.4
%âãÏÓ

3 0 obj
<<
/N 3
/Filter/FlateDecode
/Length 347
>>
stream
xÚ­‘±K1Æ¿»",­‚ƒcâ í¢ÎU,ˆ:Ô*.½ôÚ\Û#—SKGa:tQq±ø/¨“øa(‚:¹¸»"H9_ "øò~ùò^^"°´Ø<Ð÷äû/ã@ôèîøþû±ïw;@ä¸l÷Îש×ÓÏä·z^òHl§=Ï8ÎZÀÈ#÷ÄÆg©š^~]a}&EÔÔ¹aòM`áØ?built-in)
/DestOutputProfile 3 0 R
>>]
endobj
9 0 obj
<<
/Type/XObject
/Subtype/Form
/FormType 1
/BBox [-0.77592048 811.77305 29.155605 841.70457]
/Matrix [1 0 0 1 0 0]
/Resources <<
/ProcSet 1 0 R
>>
/Filter/FlateDecode
/Length 457
>>
stream
xÚm”1’Û0
...etc
1 Like

Great! It probably means that the android device you are testing on(not the emulator) doesn’t have pdf reader. You could download any pdf reader from google playstore and install it. You could then try the same thing again. It should open this time.

Your debug output should be pretty much the file. You could just copy all those data in a text editor on your dektop, save it as pdf file and see if it opens. This would just assure that the content of PDF is not the problem, just in case if the issue still persists.

Unfortunately the Android device has a default PDF reader installed, I have also installed Acrobat Reader. Also on iOS device it behaves the same way.

I tried to do it and even on PC the PDF file opens with 4 blank pages

Ok, so content is the problem. Could you recheck with the base64 data again?

How can I Check it?
That string is generated by an API in C#.

The string is the same as on an iOS device with a simple HREF (see my first post) opens and displays the PDF correctly.

Ok, it’s tricky to debug now. Could you share the base64?

Hey, did the same like you did by converting the base64 to blob after atob(), except without using the slice concept and it works well on my device. Below is my code I used:

openPDFFile(){
     this.file.createFile(this.file.externalRootDirectory,'test.pdf',true).then((response) => {
        console.log('file created',response);

        const byteCharacters = atob(this.base64_data);
        const byteNumbers = new Array(byteCharacters.length);
        for (let i = 0; i < byteCharacters.length; i++) {
            byteNumbers[i] = byteCharacters.charCodeAt(i);
        }

        const byteArray = new Uint8Array(byteNumbers);
        const blob = new Blob([byteArray], {type: 'application/pdf'});

        this.file.writeExistingFile(this.file.externalRootDirectory,'test.pdf',blob).then((response) => {
          console.log('successfully wrote to file',response);
          this.fileOpener.open(this.file.externalRootDirectory + 'test.pdf','application/pdf').then((response) => {
            console.log('opened PDF file successfully',response);
          }).catch((err) => {
              console.log('error in opening pdf file',err);
          });
        }).catch((err) => {
          console.log('error writing to file',err);
        });       

     }).catch((err) => {
        console.log('Error creating file',err);
     });
  }
1 Like

Hi @vivek-23, I try your code but doesn’t works for me :frowning: