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, } });