Vamos a crear una aplicación React Native que permita leer o escribir un archivo en un espacio de almacenamiento interno o externo. Tener acceso al archivo del sistema puede ser útil para guardar datos de una sesión de la aplicación a otra, o para utilizar o modificar los datos de un archivo.
Hardware
- Dispositivo Android
- Ordenador para programar
- Un cable USB para conectar el dispositivo Android al PC
Configuración del proyecto React Native
Para leer y escribir en un archivo, vamos a utilizar la biblioteca del sistema de archivos, react-native-fs
npm install --save react-native-fs
Utilizar una biblioteca
De la biblioteca, importamos los objetos y funciones que nos interesan
import { DocumentDirectoryPath, writeFile, readDir, readFile, unlink } from 'react-native-fs'; //send files for testing
- DocumentDirectoryPath se utiliza para recuperar la ruta a los archivos de la aplicación
- writeFile permite escribir en un archivo
- readDir lee un directorio
- readFile lee un archivo
- unkink borra un archivo
A continuación, creamos un componente funcional con los estados deseados
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([]);
A continuación, añadimos las funciones útiles para el sistema de archivos leer, escribir, borrar
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);
}
};
Cuando se crea el componente, leemos el directorio por defecto
useEffect(() => {
/* file system*/
console.log("FS directory path : ",dirPath);
readDir(dirPath).then((files) => {
console.log("files on FS: ",files);
setFileInDir(files);
});
}, [filename,dirPath]);
Por último, creamos el renderizado de la aplicación con los siguientes elementos
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>
);
Resultados
Con esta aplicación, podemos visualizar el contenido de un directorio, y leer, escribir y borrar un fichero.

Leer y escribir un archivo en un espacio de almacenamiento externo
Por defecto, una aplicación sólo tiene autoridad sobre su propio directorio
Es posible otorgar derechos de acceso a un espacio de almacenamiento externo, como una llave USB.
Para ello, puedes conceder derechos desde la interfaz de Android. Ve a Ajustes > Aplicaciones y notificaciones > Gestor de permisos > Archivos y contenido multimedia.

Conecte la memoria USB a su dispositivo, utilizando adb para encontrar la ruta al espacio de almacenamiento en ls /storage/ (ex /storage/70A8-C229)

Una vez activada la autorización, podrás ver y crear archivos en la memoria USB.

Código completo de gestión de archivos con 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,
}
});