Introduction
This is a simple example 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
Attention
- To keep it simple, I did not add any styles and only used the file names as unique identifiers.
- Please don't do this in a production app. Use GUIDs instead.
- You should split the code into different files. This example has some inline functions, that should be placed somewhere else.
Implementation
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)
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>
);
};
Top comments (0)