Skip to main content

Command Palette

Search for a command to run...

Simplifying File Download and Viewing in React Native Apps

How to download, check, and open files in React Native using react-native-blob-util, with platform-specific handling for iOS and Android.

Published
6 min read
Simplifying File Download and Viewing in React Native Apps

Originally written in 2024. Content may vary slightly across newer versions.

What is React Native, and how does it facilitate cross-platform development?

React Native enables developers to build mobile applications for both iOS and Android platforms using a single codebase.

Why is file management important in React Native development?

File management is crucial for many applications, such as handling downloads and opening files. Implementing these features requires consideration of platform-specific differences and permissions.

What tools or library should be used?

When it comes to file operations, the react-native-fetch-blob library offers robust capabilities. It provides a comprehensive set of functions for handling file downloads, uploads, and other file-related tasks in React Native applications.

File Download

Key considerations:

  • Choose an appropriate directory based on the platform.

  • Construct the file path using a unique identifier.

  • Execute platform-specific download logic, handling permissions and errors accordingly.

import { PermissionAndroid, Platform, Alert } from "react-native";
import DeviceInfo from "react-native-device-info";
import ReactNativeBlobUtil from "react-native-blob-util";
import FileViewer from "react-native-file-viewer";
import _ from "lodash";

const downloadPdfToFileSystem = async (base64, fileId) => {
  const IS_IOS = Platform.OS === "ios";
  const {
    dirs: { DownloadDir, DocumentDir },
  } = ReactNativeBlobUtil.fs;
  const dirs = IS_IOS ? DocumentDir : DownloadDir;
  const path = dirs + '/' + fileId + '.pdf';

  try {
    if (!IS_IOS) {
      const systemVersion = DeviceInfo.getSystemVersion();
      const isDownloadAllowed = await PermissionAndroid.request(
        PermissionAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE
      );

      if (
        systemVersion >= "11.0" ||
        isDownloadAllowed === PermissionAndroid.RESULTS.GRANTED
      ) {
        await ReactNativeBlobUtil.fs.writeFile(path, base64, "base64");
      }

      return path;
    }

    const ReactNativeBlobConfigs = { fileCache: true, path };
    const fileUrl = 'data:application/pdf;base64,' + base64;
    const response = await ReactNativeBlobUtil.config(
      ReactNativeBlobConfigs
    ).fetch("GET", fileUrl, {});

    return response.path();
  } catch (e) {
    console.log("PDF error ", e);
    Alert.alert(e.toString());
  }
};

To streamline file downloads, I’ve implemented a function called downloadPdfToFileSystem, which facilitates downloading PDF data in base64 format and storing it in the device’s file system.

Here’s a breakdown of the process:

1. Choose an appropriate directory based on the platform: Use DocumentDir for iOS and DownloadDir for Android.

2. Construct the file path using a unique identifier, fileId.

3. Execute platform-specific download logic:

For Android: Check the device’s system version. If it’s 11.0 or higher, no need to request WRITE_EXTERNAL_STORAGE permission; proceed with file writing. Otherwise, request permission using PermissionAndroid.request.

For iOS: Configure file cache and path using ReactNativeBlobUtil.config; Use the fetch method to retrieve the PDF file from base64 data and save it.

4. Handle errors gracefully: Log error messages to the console and display an alert dialog if any issues arise.

☀️ Highlights:

  • Choice of Directories:

Documents directory in iOS and Downloads in Android: Selected based on platform differences, permission management, and user experience considerations.

Platform Differences: iOS and Android platforms have different file system structures. iOS apps typically save user-generated files in the Documents directory, while Android apps tend to save downloadable files in the external storage’s Downloads directory.

Permission Management: Accessing external storage on Android usually requires dynamically requesting permissions, especially after Android 11 (API level 30), which imposes stricter restrictions on file access. In contrast, iOS is relatively more lenient in terms of file access permissions and does not require explicit permission requests.

User Experience: Saving downloaded files in familiar locations can enhance user experience. For example, on Android devices, saving files to the Downloads directory allows users to easily locate downloaded files in the system file manager or other apps.

  • Understanding PermissionAndroid.request

This function requests write access to external storage on Android. It’s an asynchronous operation, awaiting the user’s response, which can be granted, denied, or permanently denied (“never ask again”).

Based on the user’s response, developers can take appropriate actions, such as proceeding with the file writing operation or providing relevant prompts when the user denies the permission.

  • Data URL Explanation

A data URL is a special format containing Base64-encoded data. In this case, it specifies the MIME type (“application/pdf”) and the encoded PDF content, guiding ReactNativeBlobUtil on data handling.

  • Handling Image Downloads

For images, modify the MIME type to “image/jpeg” or “image/png” and replace “base64” with image data. This ensures proper handling by ReactNativeBlobUtil.

Check File Existence

const checkFileExistence = async (fileId) => {
  const IS_IOS = Platform.OS === "ios";
  const {
    dirs: { DownloadDir, DocumentDir },
  } = ReactNativeBlobUtil.fs;
  const dirs = IS_IOS ? DocumentDir : DownloadDir;
  const path = dirs + '/' + fileId + '.pdf';

  try {
    const isExist = await ReactNativeBlobUtil.fs.exists(path);
    return { isExist, path };
  } catch (e) {
    console.log("check file existence error ", e);
    Alert.alert(e.toString());
  }
};

This function checks the existence of a file by constructing the file path based on the provided fileId.

It uses the ReactNativeBlobUtil.fs.exists method to check if the file exists at the specified path. The result is stored in the isExist variable.

If any errors occur during the process, such as invalid file paths or permission issues, they are caught in thecatch block. The error message is logged to the console using console.logand displayed as an alert using Alert.alert.

Opening Downloaded Files

const openExistingFile = async (path, isInModalOrAlerts = false) => {
  const IS_IOS = Platform.OS === "ios";

  try {
    if (!IS_IOS) {
      return await ReactNativeBlobUtil.android.actionViewIntent(
        path,
        "application/pdf"
      );
    }

    // ReactNativeBlobUtil.ios.openDocument does not work with Modal and Alerts components
    // Reference: https://github.com/joltup/rn-fetch-blob/issues/243
    if (isInModalOrAlerts) {
      await FileViewer.open(path);
    } else {
      await ReactNativeBlobUtil.ios.previewDocument(path);
    }
  } catch (e) {
    console.log("open file error: ", e);
    Alert.alert(e.toString());
  }
};

On the Android platform, the ReactNativeBlobUtil.android.actionViewIntent method is employed to open the file. This method sends an action view intent, prompting the system to open the specified file.

On iOS, two different methods are used:

1. If the isInModalOrAlerts parameter is true, indicating that the current application context is within a modal dialog or alert, the FileViewer.open method is invoked to open the file. This is because the ReactNativeBlobUtil.ios.previewDocument method may not function properly within modal dialogs or alerts due to certain limitations.

2. If the isInModalOrAlerts parameter is false, indicating that the current application context is not within a modal dialog or alert, the ReactNativeBlobUtil.ios.previewDocument method is directly called to open the file.

In case of any errors, the error messages are logged using console.log and displayed using the Alert.alert method.

By following these guidelines, developers can simplify file download and viewing in React Native apps, ensuring smooth user experiences across iOS and Android platforms.