Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / React

React-Native FileManager Component with Firebase Storage

5.00/5 (1 vote)
18 May 2021CPOL 4K  
How to create a React-Native (iOS & Android) FileManager with Firebase Storage
This is an example component of a react-native FileManager written in Typescript. It is compatible with iOS & Android.

Introduction

This is a simple example of how to implement a file manager with React-Native, Firebase & Typescript for iOS & Android.

What I Will Cover in This Post

  • Fetch & display files from the Firebase storage
  • Open a file picker and upload the file to Firebase
  • View & download the files on iOS & Android
  • Delete files from the Firebase storage

Background

Implementing a cross-platform FileManager solution for iOS & React-Native in our mobile project for Aplano proved itself as pretty difficult. As there was no common solution for this in the web, we decided to share our code.

Using the Code

  1. To keep it simple, I did not add any styles and only used the file names as unique identifiers.
  2. Please don't do this in a production app. Use GUIDs instead.
  3. You should split the code into different files. This example has some inline functions, that should be placed somewhere else.

This component uses the following npm packages:

  • react-native-firebase
  • react-native-document-picker
  • react-native-image-picker
  • react-native-fs

(You need to follow the README setups of these npm packages in advance.)

TypeScript
import React, { useEffect, useState } from "react";
import storage, { FirebaseStorageTypes } from "@react-native-firebase/storage";
import AntDesignIcon from "react-native-vector-icons/AntDesign";
import { Text, View, TouchableOpacity, Alert, Platform, ActionSheetIOS } from "react-native";
import RNFS from "react-native-fs";
import FileViewer from "react-native-file-viewer";
import { launchImageLibrary } from "react-native-image-picker";
import DocumentPicker from "react-native-document-picker";

const pickDocument = async () => {
  const res = await DocumentPicker.pick({
    type: [DocumentPicker.types.allFiles],
  });

  return { path: res.fileCopyUri, name: res.name };
};

const pickAttachmenet = async () => {
  if (Platform.OS === "ios") {
    const options = ["Image", "Document", "Cancel"];
    ActionSheetIOS.showActionSheetWithOptions(
      { options, cancelButtonIndex: 2, title: "Pick a data type" },
      async (buttonIndex) => {
        if (buttonIndex === 0) {
          // Open Image Picker
          launchImageLibrary({ mediaType: "photo" }, (res) => {
            if (!res.didCancel) {
              return { path: res.uri, name: res.fileName };
            }
          });
        } else if (buttonIndex === 1) {
          // Open Document Picker
          return pickDocument();
        } else {
          // exit
        }
      }
    );
  } else {
    // For Android, we can just use the normal DocumentPicker, as it can also access images
    return pickDocument();
  }
};

export const FileManager = () => {
  const [uploads, setUploads] = useState<FirebaseStorageTypes.Reference[]>([]);

  useEffect(() => {
    (async () => {
      const listRef = storage().ref().child("uploads");
      const res = (await listRef.listAll()).items;
      setUploads(res);
    })();
  }, []);

  return (
    <View>
      {uploads.map((upload) => {
        return (
          <View key={upload.name}>
            <TouchableOpacity
              onPress={async () => {
                const localPath = `${RNFS.DocumentDirectoryPath}/${upload.name}`;

                await RNFS.downloadFile({
                  fromUrl: await upload.getDownloadURL(),
                  toFile: localPath,
                }).promise;

                FileViewer.open(localPath, {
                  displayName: upload.name,
                });
              }}
            >
              <Text>{upload.name}</Text>
            </TouchableOpacity>
            <AntDesignIcon
              name="delete"
              onPress={() => {
                Alert.alert(`delete ${upload.name}?`, undefined, [
                  { text: "No" },
                  {
                    text: "Yes",
                    onPress: async () => {
                      const fileRef = storage().ref().child("uploads").child(upload.name);
                      await fileRef.delete();
                      setUploads(uploads.filter((u) => u.name === upload.name));
                    },
                  },
                ]);
              }}
            />
          </View>
        );
      })}

      <TouchableOpacity
        children={<Text>Upload File</Text>}
        onPress={async () => {
          const attachmentInfo = await pickAttachmenet();
          if (attachmentInfo?.name) {
            const fileRef = 
            storage().ref().child("uploads").child(attachmentInfo.name); // Alternatively 
                                                   // use custom guids as file names
            const res = await fileRef.putFile(attachmentInfo.path);

            setUploads([...uploads, res.ref]);
          }
        }}
      />
    </View>
  );
};

History

  • 18th May, 2021: Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)