We’re going to create a React Native application that lets you read or write a file in an internal or external storage space. Having access to a system file can be useful for saving data from one application session to another, or for using or modifying data in a file.
Hardware
- Android device
- Computer for programming
- USB cable to connect Android device to PC
React Native project configuration
To read and write to a file, we’ll use the File System library, react-native-fs
npm install --save react-native-fs
Using libraries
From the library, we import the objects and functions we are interested in
import { DocumentDirectoryPath, writeFile, readDir, readFile, unlink } from 'react-native-fs'; //send files for testing
- DocumentDirectoryPath retrieves the path to application files
- writeFile allows you to write to a file
- readDir reads a directory
- readFile reads a file
- unkink deletes a file
We then create a functional component with the desired states
const FileSysComp = () => { const [dirPath, setDirPath] = useState(DocumentDirectoryPath); const [filename, setFilename] = useState("myfile.txt"); const [textToWrite, setTextToWrite] = useState(""); const [textFromFile, setTextFromFile] = useState(""); const [fileInDir, setFileInDir] = useState([]);
We then add the useful functions for the file system read, write, delete
const makeFile = async (filePath : string, content : string) => { try { //create a file at filePath. Write the content data to it await writeFile(filePath, content, "utf8"); console.log("written to file"); } catch (error) { //if the function throws an error, log it out. console.log(error); } }; const getFile = async (filePath : string) => { try{ const response = await readFile(filePath); setTextFromFile(response) console.log("File read : ",response) } catch (error) { console.log(error); } }; const deleteFile = async (filePath : string) => { try { await unlink(filePath); //delete the item present at 'path' console.log("deleted : ",filePath); } catch (error) { console.log(error); } };
When the component is created, we read the default directory
useEffect(() => { /* file system*/ console.log("FS directory path : ",dirPath); readDir(dirPath).then((files) => { console.log("files on FS: ",files); setFileInDir(files); }); }, [filename,dirPath]);
Finally, we create the application rendering with the following elements
- entry bar to specify directory
- input bar to specify file name
- text zone for text to be written
- Write button to write text to the file
- text box to display files and subdirectories in the directory
- Read button to read the text contained in the file
- text zone to display text read
- Delete button to delete the specified file
return ( <View style={styles.mainBody}> <Text style={styles.mainTitle}> AC File System </Text> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Directory" value={dirPath} onChangeText={setDirPath} /> </View> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Filename" value={filename} onChangeText={setFilename} /> </View> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Text to write" value={textToWrite} onChangeText={setTextToWrite} /> <TouchableOpacity onPress={() => makeFile(dirPath + "/" + filename, textToWrite)} style={[styles.sendButton]}> <Text style={styles.buttonText}> WRITE </Text> </TouchableOpacity> </View> <Text>Directory:</Text> <ScrollView style={styles.textOutput}> {fileInDir.length>0 ? fileInDir.map((filedir, idx) => ( <Text key={idx}>{filedir.name}</Text> )) : <Text>Folder empty or unknown</Text>} </ScrollView> <View style={{ flexDirection: 'row', justifyContent:'space-between'}}> <Text>Text from file:</Text> <TouchableOpacity onPress={() => getFile(dirPath + "/" + filename)} style={[styles.sendButton]}> <Text style={styles.buttonText}> READ </Text> </TouchableOpacity> </View> <ScrollView style={styles.textOutput}> {textFromFile ? <Text>{textFromFile}</Text> : null} </ScrollView> <TouchableOpacity onPress={() => deleteFile(dirPath + "/" + filename)} style={[styles.sendButton]}> <Text style={styles.buttonText}> DELETE </Text> </TouchableOpacity> </View> );
Results
With this application, we can display the contents of a directory, read, write and delete a file.
Read and write a file to an external storage space
By default, an application has authority only over its own directory
It is possible to give access rights to an external storage space such as a USB key.
To do this, you can grant rights from the Android interface. Go to Settings > App & Notifications > Permissions manager > Files and multimedia content
Connect the USB stick to your device, using adb to locate the storage path in ls /storage/ (ex /storage/70A8-C229)
Once authorization has been activated, you’ll be able to view and create files on the USB stick.
Complete file management code with React Native
/** * https://www.npmjs.com/package/react-native-fs?activeTab=readme * https://github.com/itinance/react-native-fs */ import React, {useState, useEffect, useCallback} from 'react'; import { View, ScrollView, Text, TextInput, TouchableOpacity, StyleSheet, Alert} from 'react-native'; import { DocumentDirectoryPath, writeFile, readDir, readFile, unlink } from 'react-native-fs'; //send files for testing let storagePath = "/storage/70A8-C229" /** * Maincomp */ const FileSysComp = () => { const [dirPath, setDirPath] = useState(storagePath) //DocumentDirectoryPath); const [filename, setFilename] = useState("myfile.txt"); const [textToWrite, setTextToWrite] = useState(""); const [textFromFile, setTextFromFile] = useState(""); const [fileInDir, setFileInDir] = useState([]); const makeFile = async (filePath : string, content : string) => { try { //create a file at filePath. Write the content data to it await writeFile(filePath, content, "utf8"); console.log("written to file"); } catch (error) { //if the function throws an error, log it out. console.log(error); Alert.alert("Cannot write file. Permission denied") } }; const getFile = async (filePath : string) => { try{ const response = await readFile(filePath); setTextFromFile(response) console.log("File read : ",response) } catch (error) { console.log(error); } }; const deleteFile = async (filePath : string) => { try { await unlink(filePath); //delete the item present at 'path' console.log("deleted : ",filePath); } catch (error) { console.log(error); } }; useEffect(() => { /* file system*/ console.log("FS directory path : ",dirPath); readDir(dirPath).then((files) => { console.log("files on FS: ",files); setFileInDir(files); }).catch((e) =>{ console.log("foler does not exist"); setFileInDir([]) }); }, [filename,dirPath]); return ( <View style={styles.mainBody}> <Text style={styles.mainTitle}> AC File System </Text> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Directory" value={dirPath} onChangeText={setDirPath} /> </View> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Filename" value={filename} onChangeText={setFilename} /> </View> <View style={styles.inputBar}> <TextInput style={styles.textInput} placeholder="Text to write" value={textToWrite} onChangeText={setTextToWrite} /> <TouchableOpacity onPress={() => makeFile(dirPath + "/" + filename, textToWrite)} style={[styles.sendButton]}> <Text style={styles.buttonText}> WRITE </Text> </TouchableOpacity> </View> <Text>Directory:</Text> <ScrollView style={styles.textOutput}> {fileInDir.length>0 ? fileInDir.map((filedir, idx) => ( <Text key={idx}>{filedir.name}</Text> )) : <Text>Folder empty or unknown</Text>} </ScrollView> <View style={{ flexDirection: 'row', justifyContent:'space-between'}}> <Text>Text from file:</Text> <TouchableOpacity onPress={() => getFile(dirPath + "/" + filename)} style={[styles.sendButton]}> <Text style={styles.buttonText}> READ </Text> </TouchableOpacity> </View> <ScrollView style={styles.textOutput}> {textFromFile ? <Text>{textFromFile}</Text> : null} </ScrollView> <TouchableOpacity onPress={() => deleteFile(dirPath + "/" + filename)} style={[styles.sendButton]}> <Text style={styles.buttonText}> DELETE </Text> </TouchableOpacity> </View> ); } export default FileSysComp; let BACKGROUND_COLOR = "#161616"; //191A19 let BUTTON_COLOR = "#346751"; //1E5128 let ERROR_COLOR = "#C84B31"; //4E9F3D let TEXT_COLOR = "#ECDBBA"; //D8E9A8 var styles = StyleSheet.create({ mainBody: { flex: 1, justifyContent: 'center', backgroundColor: BACKGROUND_COLOR}, mainTitle:{ color: TEXT_COLOR, fontSize: 30, textAlign: 'center', borderBottomWidth: 2, borderBottomColor: ERROR_COLOR, }, backgroundVideo: { borderWidth: 2, borderColor: 'red', position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, }, webVideo: { borderWidth: 2, borderColor: 'green', position: 'absolute', top: 0, left: 0, bottom: 0, right: 0, }, buttonText: { color: TEXT_COLOR, fontWeight: 'bold', fontSize: 12, textAlign: 'center', textAlignVertical: 'center', }, sendButton: { backgroundColor: BUTTON_COLOR, padding: 15, borderRadius: 15, margin: 2, paddingHorizontal: 20, right:0, }, deviceItem: { flexDirection: 'row', flex: 3, marginBottom: 2, }, deviceName: { fontSize: 14, fontWeight: 'bold', }, deviceInfo: { fontSize: 8, }, deviceButton: { backgroundColor: '#2196F3', padding: 10, borderRadius: 10, margin: 2, paddingHorizontal: 20, }, inputBar:{ flexDirection: 'row', justifyContent: 'space-between', margin: 5, }, textInput:{ backgroundColor: '#888888', margin: 2, borderRadius: 15, flex:3, }, textOutput:{ backgroundColor: '#333333', margin: 10, borderRadius: 2, borderWidth: 1, borderColor: '#EEEEEE', textAlignVertical: 'top', minHeight : 50, } });