fbpixel
Étiquettes :

Nous allons créer une application React Native qui permet de lire ou écrire un fichier dans un espace de stockage interne ou externe. Avoir accès au fichier du système peut être intéressant pour sauvegarder les données d’une session de l’application à une autre ou pour utiliser ou modifier les données d’un fichier.

Matériel

  • Appareil Android
  • Ordinateur pour la programmation
  • Un câble USB pour connecter l’appareil Android au PC

Configuration du projet React Native

Pour venir lire et écrire dans un fichier nous allons utiliser la librairie File System, react-native-fs

npm install --save react-native-fs

Utilisation de librairie

A partir de la librairie, nous importons les objets et fonctions qui nous intéresse

import { DocumentDirectoryPath, writeFile, readDir, readFile, unlink } from 'react-native-fs'; //send files for testing
  • DocumentDirectoryPath permet de récupérer le chemin d’accès des fichiers de l’application
  • writeFile permet d’écrire dans un fichier
  • readDir permet de lire un répertoire
  • readFile permet de lire un fichier
  • unkink permet de supprimer un fichier

Nous créons ensuite un composant fonctionnel avec les états désirés

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([]);

Nous ajoutons ensuite les fonctions utiles pour le système de fichier lire, écrire, supprimer

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

Lors de la création du composant, nous venons lire le répertoire défini par défaut

  useEffect(() => {
    /* file system*/
    console.log("FS directory path : ",dirPath);
    readDir(dirPath).then((files) => {
      console.log("files on FS: ",files);
      setFileInDir(files);
    });
  }, [filename,dirPath]);

Enfin, nous créons le rendu de l’application avec les éléments suivant

  • barre d’entrée pour spécifier le répertoire
  • barre d’entrée pour spécifier le nom du fichier
  • zone de texte pour le texte à écrire
  • bouton Write pour écrire le texte dans le fichier
  • zone de texte pour afficher les fichiers et sous-répertoires contenu dans le répertoire
  • bouton Read pour lire le texte contenu dans le fichier
  • zone de texte pour afficher le texte lu
  • bouton Delete pour effacer le fichier spécifié
  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>
  );

Résultat

Grâce à cette application, nous pouvons afficher le contenu d’un répertoire, lire, écrire et supprimer un fichier

react-native-read-write-file-app Lire et écrire un fichier avec React Native

Lire et écrire un fichier dans un espace de stockage externe

Par défaut, une application n’a d’autorité que sur son propre répertoire

Il est possible de donner les droits d’accès à un espace de stockage externe comme une clé USB.

Pour se faire, vous pouvez accorder les droits depuis l’interface Android. Aller dans les paramètres > App et notifications > Gestionnaire des autorisations > Fichiers et contenus multimédia

android-file-storage-permission Lire et écrire un fichier avec React Native

Brancher la clé USB à votre appareil, à l’aide d’adb repérer le chemin d’accès de l’espace de stockage dans ls /storage/ (ici: /storage/70A8-C229)

android-adb-external-storage Lire et écrire un fichier avec React Native

Une fois l’autorisation activée, vous pourrez afficher et créer des fichiers sur la clés USB

react-native-read-write-usb-storage Lire et écrire un fichier avec React Native

Code complet de gestion de fichier avec 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,
  }

});

Sources