What is the procedure for opening and displaying a base64-encoded PDF within my Cordova application?

I am currently developing an Android App using Cordova, and my goal is to open and display a file (PDF or image) that is retrieved from the server as Base64-encoded binary data. Despite going through various posts on this topic within this platform, none of the suggested solutions seem to work for me. Here are more details regarding my situation:

Specifically, the server sends a JSON-file to the app, which includes a string containing the base64-encoded content of a PDF file among other things. My objective is to convert this data back into the original PDF format and present it to the user.

If this were a regular web page, I could easily embed my base64 data into a data-URL, set this as the href of an anchor tag, and add a download attribute. Alternatively, I could bundle all the data into a blob and create an object URL for it first before displaying.

In Cordova, these methods do not yield the desired results. Clicking the <a> element does not trigger any action. Here's what I have tried so far:

  • Utilizing the file plugin, I managed to write the binary data to a file on the device. While I confirmed successful downloading via a terminal, the file was saved in an app-private directory inaccessible through normal means such as the file explorer.
  • Access to the user's "downloads" folder is restricted by the file system.
  • Using window.open with the file path as the argument and "_system" as the target fails to produce any outcome. There are no errors reported but nothing happens either. Changing the target to "_blank" resulted in an ACCESS_DENIED error.
  • The use of cordova.InAppBrowser led to similar outcomes as window.open.
  • When attempting to utilize the file-opener2 plugin, the compile process failed as the plugin requires an android4 toolchain while I am targeting android 9 and above.
  • A similar issue arose when trying to employ the document-viewer plugin for PDFs exclusively; compilation proved unsuccessful.
  • Directly passing the data-URI to window.open or cordova.InAppBrowser considerably delayed loading and eventually displayed an error stating the desired page couldn't be loaded.

The test PDF file I'm working with converts to roughly 17kb after encoding into base64. While this exceeds the specified limit for data-URIs, Chrome functions without issues when handling this size. Additionally, even a significantly shorter URI behaves in the same manner.

My preferred approach would involve downloading the file and then prompting the user's standard browser to open the file automatically. This method would eliminate MIME type concerns and ensure consistency with the user's expectations. Alternatively, if this doesn't prove viable, I would settle for downloading the file into a system-wide directory and allowing the user to open it manually. Although not ideal, I would accept this compromise.

Finally, if there exists a suitable plugin or alternative solution catering specifically to PDFs, I can devise a separate strategy for images (e.g., incorporating a new <img> tag into the app and assigning the appropriate URI).

Your input on overcoming this challenge would be greatly appreciated. Below is the code segment I presently employ for file downloads:

Thank you for your consideration.

var filePath = cordova.file.externalDataDirectory;
var fileName = "someFileName.pdf";
var mime = "application/pdf";
var dataBlob = /* blob containing the PDF binary data */

function writeFile(fileEntry, dataBlob) {
    fileEntry.createWriter(function (fileWriter) {

        fileWriter.onwriteend = function() {
            console.log("Successful file write...");
            readFile(fileEntry);
        };

        fileWriter.onerror = function (e) {
            console.log("Failed file write: " + e.toString());
        };

        fileWriter.write(dataBlob);
    });
}

window.resolveLocalFileSystemURL(
    filePath,
    function onResolveSuccess (dirEntry) {
        dirEntry.getFile(
            fileName,
            { create: true },
            function onGetFileSuccess (file) (
                writeFile(file, dataBlob);
                window.open(file.toURL(), "_system"); // This line does not achieve the intended result, and the reason remains unclear.
            }
        );
    }
);

Answer №1

Successfully resolved the issue at hand.

According to the guidelines provided by the file-opener2 plugin documentation, it is essential to include the androidx-adapter plugin in order to rectify outdated (android 4) packages. By incorporating both the file-opener2 and androidx-adapter plugins, the complete code appears as follows:

var filePath = cordova.file.externalDataDirectory;  // Note: documentsDirectory is empty due to Cordova's restrictions
var fileName = "someFileName.pdf";
var mime = "application/pdf";
var dataBlob = /* binary data for a PDF stored within a blob */

function writeFile(fileEntry, dataBlob) {
    fileEntry.createWriter(function (fileWriter) {

        fileWriter.onwriteend = function() {
            console.log("File write successful...");
            readFile(fileEntry);
        };

        fileWriter.onerror = function (e) {
            console.log("Failed to write file: " + e.toString());
        };

        fileWriter.write(dataBlob);
    });
}

window.resolveLocalFileSystemURL(
    filePath,
    function onResolveSuccess(dirEntry) {
        dirEntry.getFile(
            fileName,
            { create: true },
            function onGetFileSuccess(file) {
                writeFile(file, dataBlob);
                cordova.plugins.fileOpener2.open(
                    filepath + filename,
                    mime,
                    {
                        error : function(){ },
                        success : function(){ }
                    }
                );
            }
        );
    }
);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What is the best way to choose the current Div's ID, as well as its Width and Height properties?

Within this section, there are four div elements with varying widths, heights, and colors that appear and disappear when their respective buttons are clicked. I am adding an "activeDiv" class to the visible div in order to easily select it using that class ...

masteringnode: overcoming async conundrum

I'm currently grappling with the Juggling Async challenge in learnyounode. Here's what I've attempted so far, but unfortunately, I haven't achieved the expected output. While I could easily search for the solution online, my main goal ...

JavaScript: Eliminate a specific element and retrieve the modified array

Is there a way to remove only one instance of an item from an array, even if there are multiple duplicates of that item? For example: let array = ["abc", "def", "ghi", "def"]; const toRemove = "def"; I attempted to find the index and splice the array, but ...

The jQuery remove function will only take effect on the second click following an AJAX request

I'm facing an issue with my jQuery code where two divs contain lists of links, triggering AJAX calls to append JSON data to a separate div. Upon clicking a link, the corresponding link div should hide. There's also a third div with the class "pan ...

The process of inputting values into a field using sendkeys() in Appium for Android is proving to be quite time-consuming

Struggling to input data into a field, as it is taking an unusually long time of 3-4 minutes when using sendKeys. I have attempted various methods like raw waits, implicit and explicit waits for the element, changing locator strategies, but to no avail. ...

Event that occurs when modifying a user's Firebase Authentication details

Monitoring User Actions with Firebase Authentication Within my application built using Angular, Node.js, and Firebase, I am seeking a method to track user events such as additions, modifications, and deletions. Is there a mechanism to recognize when a us ...

Failure to make an API call in React

I am just starting out with React and working on a basic app that lets you search for a name in a list of transactions and then displays the transaction details. I have been facing difficulties with handling API requests using superagent. Here is the code ...

The code to assign a value to "parentId1" using window.opener.document.getElementById is malfunctioning

Trying to retrieve the value from my child.jsp and pass it to my parent.jsp using window.opener.document.getElementById("parentId1").value = myvalue; Despite no errors appearing in the console, the value is not successfully transferring to the parent pag ...

Instructions on how to submit a form using a button while also clicking on an anchor link simultaneously

I have a form with a button to submit it. Now, I want to pass the same variables to another page in order to insert them into a database. The approach I'm considering is passing the variables using an anchor link with GET parameters. However, I' ...

What is the best way to categorize multiples of a JSON array into separate objects?

Currently, I am working with an array of JSON objects that need to be grouped together in sets of four to create a single object. This is crucial for my goal of using OPENJSON in SQL Server to insert the values into a table, where each object represents a ...

options for adaptive web layout

My goal is to ensure my website is responsive by utilizing HTML5, CSS3, JavaScript, and jQuery. I am facing an issue with the responsiveness of my menu in the CSS code. PRÉSENTATION OFFRES PRODUITS RÉFÉRENCE CONTACT Menu nav { ...

Tips on avoiding duplicate selection of checkboxes with Vue.js

Recently delving into vue.js, I encountered a challenge with multiple checkboxes sharing the same value. This resulted in checkboxes of the same value being checked simultaneously. How can this issue be resolved? var app = new Vue({ el: '#app&apo ...

What is the best way to have text automatically selected upon clicking a category in a combobox? (Vue.JS 2)

I am currently working with a Vue component that looks like this: <script> export default{ template: '\ <select class="form-control" v-model="selected" v-on:change="search">\ <option v- ...

What steps can I take to design a functional hamburger menu?

After multiple attempts to create a hamburger menu, I have hit a roadblock. Clicking on the menu icon transforms it into an 'X', but unfortunately, it does not trigger the opening of the menu. Despite days of effort and research, I have yet to fi ...

What sets apart object destructuring from destructuring assignment?

Consider this scenario where we have an object: let obj = { a: 1, b: 2 } let { a, b } = obj; console.log(a, b); // output 1, 2 Now, let's examine a different case where 'a' and 'b' are already initialized: let obj = { a: 1, b: 2 ...

Tips for implementing a handler on a DOM element

$(document).ready(function(){ var ColorBars = document.getElementsByClassName("color-bar"); var number = 0; ColorBars[0].onclick = hideLine(0); function hideLine(index){ var charts = $("#line-container").highcharts(); v ...

Navigating through pages in a server component using Next.js

I am currently working on a project that involves implementing pagination using the NextJS 13 server component without relying on the use client. The goal is to ensure that when a button is clicked, new entries are added to the screen in a sequential order ...

Java com.google.gdata.util.ParseException error: Google Spreadsheet API is unable to recognize content type application/binary

In my Android application, I am utilizing the Google Spreadsheet API. Take a look at the following code snippet: URL spreadSheetUrl = new URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full"); SpreadsheetQuery query = new SpreadsheetQuery ...

The issue with ThreeJS where the camera movement stops working after clicking

Seeking assistance with implementing camera movement in ThreeJS. Despite having no errors, clicking the button does not trigger the camera movement as expected. The function aimed at moving the camera by 200 units in all directions seems to have no effec ...

An issue arises when ng-pattern fails to recognize a regular expression

My form includes inline validation for a specific input field. <form name="coForm" novalidate> <div> <label>Transaction: </label> <input type="text" name="transaction" class="form-control" placeholder="<Dir ...