Issue with RN-fetch-blob regarding base64 encoding specifically on Android devices

I've encountered an issue in React Native where I'm trying to download a base 64 string from an API and display it as a PDF on mobile devices.

The problem arises when using the code on Android, as it returns a 'bad base 64' / invalid PDF format error, whereas it works perfectly fine on iOS.

Below is the code snippet:

//fetch on button click
 getBill = () => {
    if (Platform.OS === "android") {
      this.getAndroidPermission();
    } else {
      this.downloadBill();
    }
  };

//check user permissions on device (specifically for android)
getAndroidPermission = async () => {
    try {
      const granted = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
      );
      const grantedRead = await PermissionsAndroid.request(
        PermissionsAndroid.PERMISSIONS.READ_EXTERNAL_STORAGE
      );

      if (
        granted === PermissionsAndroid.RESULTS.GRANTED &&
        grantedRead === PermissionsAndroid.RESULTS.GRANTED
      ) {
        this.downloadBill();
      } else {
        Alert.alert(
          "Permission Denied!",
          "You need to give storage permission to download the file"
        );
      }
    } catch (err) {
      console.warn(err);
    }
  };

//download and display bill
downloadBill = async () => {
    this.setState({ loading: true });
    let billId = this.state.billId;
    let user = await AsyncStorage.getItem("user");
    let parseUser = JSON.parse(user);
    let userToken = parseUser.token;

    RNFetchBlob.config({
      addAndroidDownloads: {
        useDownloadManager: true,
        notification: true,
        path:
          RNFetchBlob.fs.dirs.DownloadDir +
          "/" +
          `billID_${this.state.billId}.pdf`,
        mime: "application/pdf",
        description: "File downloaded by download manager.",
        appendExt: "pdf",
        trusty: true,
      },
    })
      .fetch("GET", `${config.apiUrl}/crm/getbillfile/${billId}`, {
        Authorization: "Bearer " + userToken,
      })
      .then((resp) => {
        let pdfLocation =
          RNFetchBlob.fs.dirs.DocumentDir +
          "/" +
          `billID_${this.state.billId}.pdf`;

        RNFetchBlob.fs.writeFile(pdfLocation, resp.data, "base64");

        FileViewer.open(pdfLocation, {
          onDismiss: () => this.setState({ loading: false }),
        });
      });
  }; 

Android manifest:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" /> 
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    <action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
  </intent-filter>

Your assistance in resolving this issue would be greatly appreciated. Thank you!

Answer №1

The file path provided for opening in Android is incorrect, unlike in IOS where it opens from the correct location.

Make sure to check the operating system before attempting to open the file.

....
const fpath = `${RNFetchBlob.fs.dirs.DownloadDir}${filename}`;
RNFetchBlob.config({
        addAndroidDownloads: {
            useDownloadManager: true,
            notification: true,
            path: fpath,
            description: "File downloaded via download manager.",
        },
    })
    .fetch("GET", `${config.apiUrl}/crm/getbillfile/${billId}`, {
        Authorization: "Bearer " + userToken,
    })
    .then((resp) => {
        if (OS == "ios") {
            // ... existing logic goes here    
        } else if (OS === "android") {
            // Use FileViewer or RNFetchBlob to handle this.
            RnFetchBlob.android.actionViewIntent(fpath, "application/pdf");
        }
    });
};

Answer №2

finally, I have discovered it!

This is the solution

import React from 'React'

    if (Platform.OS == "android") {

        RNFetchBlob.fs.readFile(uri, 'base64')
            .then((base64Data) => {
                onPostMessage({ type: 'photo', data: `data:image/jpeg;base64,${base64Data}` });
            })
            .catch((error) => console.log(error))
        return;
    }

    RNFetchBlob.fetch('GET', uri)
        .then(response => response.base64())
        .then(base64Data => {
            onPostMessage({ type: 'photo', data: `data:image/jpeg;base64,${base64Data}` });
        })
        .catch(error => {
            console.log(error);
        });

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

Efficiently communicating updates to clients after executing multiple HTTP requests simultaneously in RxJS

Objective: Execute multiple asynchronous HTTP requests simultaneously with RxJS and trigger a callback after each request is completed. For instance: fetchData() { Observable.forkJoin( this.http.get('/somethingOne.json').map((res:Re ...

Concealing divs without values in ASP.NET MVC

I am working on an AJAX call to fetch data from the back-end and populate divs with it. Below is my code for the AJAX call: $(document).ready(function() { question_block(); }); function question_block() { $.ajax({ url: '@Url.Action(" ...

concerning a snippet of programming code

I'm new to coding and I'd like some help understanding this piece of code, particularly the red colored fonts. Which page value are they referring to? $(function() { $("#title").blur(function() { QuestionSuggestions(); }); }); funct ...

Exploring the power of React Hooks with the useState() function and an array filled

When creating new "Todo" items and modifying the fields, everything works fine. However, my problem arises when trying to retrieve the child data after clicking the "show all objects" button. To better understand my issue, here is a code example: const Co ...

API Post request encountering fetch failure

The route "/api/users/register" on my express server allows me to register an account successfully when passing data through Postman. However, when trying to register an account using the front-end React app, I'm encountering a "TYPE ERROR: Failed to ...

onmouseleave event stops triggering after blur event

I am facing an issue with a mouseleave event. Initially, when the page loads, the mouseleave event functions correctly. However, after clicking on the searchBar (click event), and then clicking outside of it (blur event), the mouseleave functionality stops ...

You are limited to storing only up to 2 items in the localStorage

My goal is to save items in local storage as an array of objects. Initially, it works perfectly and stores the first element in local storage as needed. However, I am facing an issue where I cannot store more than one element. Below is the code block that ...

aws-lambda Module Not Found

I am encountering an issue in the aws-lambda console every time I try to upload code from a zip file. Oddly, other zip files seem to work fine. The .js file within the problematic zip is named "CreateThumbnail.js" and I have confirmed that the handler is ...

Tips for ensuring form elements do not contain white space before submitting through AJAX

Before sending my form using AJAX request, I want to validate the form elements. Even though it checks for errors, the form still gets submitted. This is my code: $('#accountFormAddEdit').on('submit', function(e){ e.preventDef ...

The JSON array being returned to the Android platform is missing some vital information

I'm currently developing an application using PHP, Laravel, and Android technologies. In this setup, Laravel serves JSON data from the server (which also requires a website), while the Android app acts as the consumer. My current task involves fetch ...

Is there a Javascript library available that can generate calendar links for Google, Yahoo, Outlook, and iCal?

I recently came across a sleek and professional web page widget that includes links (for example, and meetup.com). Could someone assist me in finding the library responsible for creating these links? I explored the discussion on Need a service that build ...

What is the reason behind being able to assign unidentified properties to a literal object in TypeScript?

type ExpectedType = Array<{ name: number, gender?: string }> function go1(p: ExpectedType) { } function f() { const a = [{name: 1, age: 2}] go1(a) // no error shown go1([{name: 1, age: 2}]) // error displayed ...

The color of the letters from the user textbox input changes every second

My task is to create a page where the user enters text into a textbox. When the user clicks the enter button, the text appears below the textbox and each letter changes color every second. I am struggling with referencing this jQuery function $(function() ...

How to properly structure a Vue file in vscode to meet eslint guidelines

Essentially, what I am seeking is uniformity in: the way vscode formats my .vue file how the eslint checker scans my .vue file when I execute npm run .... to launch the server or build the target Currently, after formatting my document in vscode and then ...

Unveiling the mystery of extracting information from a string post serialization

When working with a form, I am using this jQuery code to fetch all the field values: var adtitletoshow = $("#form_data").serialize(); After alerting adtitletoshow, it displays something like this - &fomdata1=textone&fomdata2=texttwo&fomdat ...

Refreshing data attribute following an ajax request

I have this page with a list of various job listings. Each job listing has a button labeled "Paid" that includes a data-paid attribute indicating whether the job is paid or not. Whenever the "Paid" button is clicked, it sends a request to my route which u ...

The challenge of maintaining consistency in Vue 3 when it comes to communication between

Context In my Vue 3 application, there is a HomeView component that contains the following template structure: <InputsComponent></InputsComponent> <CheckboxesComponent></CheckboxesComponent> <Toolbar></Toolbar> T ...

Instead of using setTimeout in useEffect to wait for props, opt for an alternative

Looking for a more efficient alternative to using setTimeout in conjunction with props and the useEffect() hook. Currently, the code is functional: const sessionCookie = getCookie('_session'); const { verifiedEmail } = props.credentials; const [l ...

Catching the Selenium NoSuchElementError in JavaScript is impossible

Update: It's puzzling why this was marked as answered since the linked questions don't address the issue at hand and do not involve Javascript. My objective is to detect this error, rather than prevent it, given that methods like invisibilityOfEl ...

What is the syntax for requesting a JSONArray in an Ajax call using jQuery?

After browsing through numerous resources like this, this, and this, I finally landed on this. The main objective is to iterate over the JSON data returned from an Ajax call, which is encoded using json_encode in PHP. When I inspect the object using cons ...