How to save a PDF from a base64 string on the device with Cordova

For some developers, it's easy to return a base64 string of files from the server. Normally, PDF files are allowed to be viewed in your desktop browser using a simple window.open function :

window.open("data:application/pdf;base64,JVBERi0xLjcKCjEgMCBv...","_blank");

However, in cordova this doesn't works. You'll need to save the PDF on your device if you want to see it and use a plugin like cordova-plugin-fileopener2 to view it.

Cordova by itself doesn't allow the preview directly in your application, even if you use the inapp-browser plugin.

Requirements

As we are handling files, you need to have the cordova-file-plugin in your project. To install this plugin use :

cordova plugin add cordova-plugin-file

If you don't know anything about this plugin, read more about how it works in the official repository here.

Implementation

In order to write the PDF base64 string as a file on the device, we'll use the following two methods :

/**
 * Convert a base64 string in a Blob according to the data and contentType.
 * 
 * @param b64Data {String} Pure base64 string without contentType
 * @param contentType {String} the content type of the file i.e (application/pdf - text/plain)
 * @param sliceSize {Int} SliceSize to process the byteCharacters
 * @see http://stackoverflow.com/questions/16245767/creating-a-blob-from-a-base64-string-in-javascript
 * @return Blob
 */
function b64toBlob(b64Data, contentType, sliceSize) {
        contentType = contentType || '';
        sliceSize = 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;
}

/**
 * Create a PDF file according to its database64 content only.
 * 
 * @param folderpath {String} The folder where the file will be created
 * @param filename {String} The name of the file that will be created
 * @param content {Base64 String} Important : The content can't contain the following string (data:application/pdf;base64). Only the base64 string is expected.
 */
function savebase64AsPDF(folderpath,filename,content,contentType){
    // Convert the base64 string in a Blob
    var DataBlob = b64toBlob(content,contentType);
    
    console.log("Starting to write the file :3");
    
    window.resolveLocalFileSystemURL(folderpath, function(dir) {
        console.log("Access to the directory granted succesfully");
		dir.getFile(filename, {create:true}, function(file) {
            console.log("File created succesfully.");
            file.createWriter(function(fileWriter) {
                console.log("Writing content to file");
                fileWriter.write(DataBlob);
            }, function(){
                alert('Unable to save file in path '+ folderpath);
            });
		});
    });
}

The cordova file writer provided by the cordova-file-plugin doesn't support write files with base64, therefore we are going to use a little trick. The base64 string will be processed and converted into a writable Blob.

Using savebase64AsPDF method

The method is well explained in the snippet, to test it you can test the following code in your project, it should create a hello world PDF in your root directory.

// Remember to execute this after the onDeviceReady event

// If your base64 string contains "data:application/pdf;base64,"" at the beginning, keep reading.
var myBase64 = "JVBERi0xLjcKCjEgMCBvYmogICUgZW50cnkgcG9pbnQKPDwKICAvVHlwZSAvQ2F0YWxvZwogIC9QYWdlcyAyIDAgUgo+PgplbmRvYmoKCjIgMCBvYmoKPDwKICAvVHlwZSAvUGFnZXMKICAvTWVkaWFCb3ggWyAwIDAgMjAwIDIwMCBdCiAgL0NvdW50IDEKICAvS2lkcyBbIDMgMCBSIF0KPj4KZW5kb2JqCgozIDAgb2JqCjw8CiAgL1R5cGUgL1BhZ2UKICAvUGFyZW50IDIgMCBSCiAgL1Jlc291cmNlcyA8PAogICAgL0ZvbnQgPDwKICAgICAgL0YxIDQgMCBSIAogICAgPj4KICA+PgogIC9Db250ZW50cyA1IDAgUgo+PgplbmRvYmoKCjQgMCBvYmoKPDwKICAvVHlwZSAvRm9udAogIC9TdWJ0eXBlIC9UeXBlMQogIC9CYXNlRm9udCAvVGltZXMtUm9tYW4KPj4KZW5kb2JqCgo1IDAgb2JqICAlIHBhZ2UgY29udGVudAo8PAogIC9MZW5ndGggNDQKPj4Kc3RyZWFtCkJUCjcwIDUwIFRECi9GMSAxMiBUZgooSGVsbG8sIHdvcmxkISkgVGoKRVQKZW5kc3RyZWFtCmVuZG9iagoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDEwIDAwMDAwIG4gCjAwMDAwMDAwNzkgMDAwMDAgbiAKMDAwMDAwMDE3MyAwMDAwMCBuIAowMDAwMDAwMzAxIDAwMDAwIG4gCjAwMDAwMDAzODAgMDAwMDAgbiAKdHJhaWxlcgo8PAogIC9TaXplIDYKICAvUm9vdCAxIDAgUgo+PgpzdGFydHhyZWYKNDkyCiUlRU9G";
// To define the type of the Blob
var contentType = "application/pdf";
// if cordova.file is not available use instead :
// var folderpath = "file:///storage/emulated/0/";
var folderpath = cordova.file.externalRootDirectory;
var filename = "helloWorld.pdf";

savebase64AsPDF(folderpath,filename,myBase64,contentType);

Cordova android pdf base64

According to your case and base64 you can have 2 types of base64 strings :

// The entire base64 string that can be viewed in the browser as pdf
var type1 = "data:application/pdf;base64,JVBERi0xLjcKCjEgMCBv.......";
// Only the base64 string without specific format
var type2 = "JVBERi0xLjcKCjEgMCBv.......";

Saving base64 type 1

As this string already contains the content type that requires the function, we only need to process the string and retrieve in different variables the content and the content type (application/pdf).

/** Process the type1 base64 string **/
var myBaseString = "data:application/pdf;base64,JVBERi0xLjcKCjEgMCBv.......";

// Split the base64 string in data and contentType
var block = myBaseString.split(";");
// Get the content type
var dataType = block[0].split(":")[1];// In this case "application/pdf"
// get the real base64 content of the file
var realData = block[1].split(",")[1];// In this case "JVBERi0xLjcKCjE...."

// The path where the file will be created
var folderpath = "file:///storage/emulated/0/";
// The name of the PDF
var filename = "mypdf.pdf";

savebase64AsPDF(folderpath,filename,realData,dataType);

Saving base64 type 2

As we know the mime type of a PDF, we don't need to retrieve from the entire base64 string, therefore just proceed to use the method and save it :

// The base64 content
var myBase64 = "JVBERi0xLjcKCjE....";
// Define the mimetype of the file to save, in this case a PDF
var contentType = "application/pdf";
// The path where the file will be saved
var folderpath = "file:///storage/emulated/0/";
// The name of your file
var filename = "myPdf.pdf";

savebase64AsPDF(folderpath,filename,myBase64,contentType);

Have fun !

Become a more social person