web-dev-qa-db-fra.com

Comment FileReader.readAsText dans l'API de fichier HTML5 fonctionne?

J'ai écrit le code suivant pour vérifier si le fichier téléchargé existe ou non en utilisant l'API de fichier HTML5.

<input type="file" id="myfile">
<button type="button" onclick="addDoc()">Add Document</button>
<p id="DisplayText"></p>

Le code JavaScript suivant qui lui a été mappé est le suivant:

function addDoc() {
  var file=document.getElementById("myFile").files[0]; //for input type=file
  var reader=new FileReader();
  reader.onload = function(e) {}
  reader.readAsText(file);
  var error = reader.error;
  var texte=reader.result;
  document.getElementById("DisplayText").innerText=reader.result; /*<p id="DisplayText>*/
}

Après avoir parcouru un fichier du système local, j'ai essayé de supprimer le document "parcouru" du dossier avant de cliquer sur addDoc(). Après avoir cliqué sur le bouton, je pouvais toujours voir que Filereader.result N'était pas nul et pouvait afficher tout le contenu.

Quelqu'un peut-il expliquer comment fonctionne Filereader? Est-ce que FileReader est lié dès que le fichier est parcouru?

Pouvons-nous également vérifier si le système Readonly Attribut avec FileReader similaire à Java File.canread()?

Quelqu'un pourrait-il suggérer cela? J'ai IE11 pour tester le code.

11
sushmithaP

L'événement FileReaderload définit la valeur .result De manière asynchrone. Pour accéder à l'événement .result, Utilisez l'événement load ou loadend.

Lorsqu'un fichier a été sélectionné dans l'interface utilisateur <input type="file">Choose File Ou Browse..., La suppression du fichier sur le système de fichiers local ne devrait pas affecter l'objet File dans FileList retourné par l'appel de .files. Voir 2.9.2. Objets transférables , 6.7.3 L'interface DataTransfer .

4. L'interface Blob et les données binaires

Chaque Blob doit avoir un état d'instantané interne , qui doit être initialement défini sur l'état du stockage sous-jacent, si un tel stockage sous-jacent existe, et doit être conservé via structured clone . Une définition normative supplémentaire de snapshot state peut être trouvée pour File s.

2.9.8 Patch Monkey pour les objets Blob et FileList

Ce patch de singe sera supprimé en temps voulu. Voir w3c/FileAPI numéro 32 .

Les objets Blob sont cloneable objects .

  1. Chaque méthode interne [[ Blob ]] de chaque objet Clone, étant donné targetRealm et ignorant la mémoire, doit exécuter ces étapes:

  2. Si c'est closed , lancez alors "DataCloneError"DOMException .

Renvoie une nouvelle instance de this in targetRealm, correspondant aux mêmes données sous-jacentes.

FileList les objets sont objets clonables .

La méthode interne [[Clone]] De chaque objet FileList, étant donné targetRealm et memory, doit exécuter ces étapes:

  1. Soit sortie un nouvel objet FileList dans targetRealm.

  2. Pour chaque fichier dans ceci , ajoutez? [StructuredClone][15](_file, targetRealm, memory_) à la fin de la liste des File objets de sortie.

Retour sortie.


Sélection de fichiers ou dossiers en lecture seule sur les navigateurs webkit et firefox

Sur chrome, chrome si l'autorisation en lecture seule est définie pour le fichier sur le système de fichiers local et que l'utilisateur sélectionne le fichier à l'élément <input type="file">, Où FileReader est utilisé pour lire le fichier, une erreur est lancée sur FileReader, généré à partir de l'événement FileReaderprogress.

Si un Blob URL Est défini sur le même objet fichier, l'URL blob: Ne renverra pas le fichier en lecture seule à la demande au Blob URL.

Sélection du dossier dans lequel l'autorisation de dossier est définie en lecture seule

Chrome, chrome

Chez chrome, chrome où l'attribut webkitdirectory est défini et le dossier est sélectionné avec une autorisation en lecture seule FileList.length De event.target.files Renvoyé 0; event.target.files.webkitGetAsEntry() n'est pas appelée, "No file chosen" est rendu à <input type="file">shadowDOM. Lorsqu'un dossier est déposé à <input type="file"> Ou à l'élément où droppable est défini, le répertoire .name Et .path Du dossier en lecture seule s'affiche à dropevent.dataTransfer.

Lorsque l'utilisateur dépose un fichier ou un dossier à l'élément <textarea>, Où aucun événement drop n'est attaché beforeunload est appelé et une invite est affichée dans l'interface utilisateur

Do you want to leave this site?
Changes you made may not be saved.
<Stay><Leave> // <buttons>

Firefox

Sur la version 47.0b9 de Firefox avec l'attribut allowdirs, l'élément <input type="file"> Est défini, où l'utilisateur clique sur "Choose folder.."<input>, Le dossier .name Et .path Du dossier parent est accessible à .then() chaîné à event.target.getFilesAndDirectories(). Les fichiers ou dossiers contenus dans le dossier sélectionné ne sont pas renvoyés lors de l'itération récursive des entrées Directory; une chaîne vide est retournée.

Si l'utilisateur clique sur "Choose file..."<input> Et qu'un dossier est sélectionné sans autorisation en lecture seule, lorsque vous cliquez sur le dossier du gestionnaire de fichiers, les fichiers du dossier sont répertoriés.

Lorsqu'un dossier est sélectionné dans lequel une autorisation de lecture seule est définie, une notification alert() est rendue lors de l'affichage de l'interface utilisateur

  Could not read the contents of <directory name>
  Permission denied

Bug, problème de sécurité

* nix OS

Lorsque l'utilisateur supprime le dossier à l'élément <textarea>, Où aucun événement drop n'est attaché, le chemin d'accès complet au dossier du système de fichiers utilisateur file: Est exposé. Les chemins d'accès aux fichiers contenus dans le dossier ne sont pas également définis comme .value; par exemple.,

"file:///home/user/Documents/Document/"

Lorsqu'un fichier est déposé à l'élément <textarea>, Où aucun événement drop n'est attaché, le chemin d'accès complet au fichier sur le système de fichiers utilisateur est défini comme .value De <textarea>; C'est,

"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue.txt"

Si plusieurs fichiers sont sélectionnés et déposés à l'élément <textarea>, Tous les chemins de fichiers complets sont définis comme .value De <textarea>, Délimités par un nouveau caractère de ligne \n

"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue1.txt"
"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue2.txt"
..

Où une XMLHttpRequest() est créée pour le chemin du fichier et l'erreur est enregistrée dans console

NS_ERROR_DOM_BAD_URI: Access to restricted URI denied

Lorsqu'il est défini comme .src D'un élément <img> Avec .crossOrigin Réglé sur "anonymous" Le gestionnaire d'événements imgerror est appelé

Lors de l'appel à window.open() avec le chemin complet défini au premier paramètre

Error: Access to '"file:///home/user/Documents/Document/MyFileFullPathDisplayedAtTextAreaValue.png"' from script denied

Spécification

4.10.5.1.18. État de téléchargement du fichier (type=file)

EXEMPLE 16

Pour des raisons historiques, l'attribut value IDL préfixe le nom du fichier avec la chaîne "C:\fakepath\". Certains agents utilisateurs hérités incluaient en fait le chemin complet (qui était une vulnérabilité de sécurité). Par conséquent, l'obtention du nom de fichier à partir de l'attribut IDL value d'une manière rétrocompatible n'est pas anodine.

4.10.5.4. API élémentaires <input> Communes

nom de fichier

Lors de l'obtention, il doit renvoyer la chaîne "C:\fakepath \" suivie du nom du premier fichier de la liste selected files , le cas échéant, ou la chaîne vide si le la liste est vide. Lors du réglage, si la nouvelle valeur est la chaîne vide, elle doit vider la liste de selected files; sinon, il doit lancer un "InvalidStateError" DOMException.

REMARQUE: Cette exigence de "fakepath" est un triste accident de l'histoire. Voir l'exemple dans la section File Upload state pour plus d'informations.

REMARQUE: Étant donné que path components Ne sont pas autorisés dans les noms de fichiers dans la liste de selected files, Le "\fakepath\ "ne peut pas être confondu avec un composant de chemin.

4.10.5.1.18. État de téléchargement du fichier (type=file)

Composants du chemin

Lorsqu'un attribut type d'un élément <input> Est à l'état File Upload, Les règles de cette section s'appliquent.

L'élément <input>represents une liste de selected files, Chaque fichier comprenant un nom de fichier, un type de fichier et un corps de fichier (le contenu du fichier).

Les noms de fichiers ne doivent pas contenir path components , même dans le cas où un utilisateur a sélectionné une hiérarchie de répertoires entière ou plusieurs fichiers portant le même nom dans différents répertoires. Les composants de chemin , aux fins de l'état File Upload, Sont les parties des noms de fichiers qui sont séparées par le caractère U + 005C REVERSE SOLIDUS () personnages.

Rapport de bogue https://bugzilla.mozilla.org/show_bug.cgi?id=131182


Suppression d'un fichier dans <textarea> à l'URI de données

Suite au commentaire de Neal Deakin au rapport de bug

Je pense que les étapes mentionnées sont:

  1. Données ouvertes: texte/html,
  2. Faites glisser un fichier du bureau vers la zone de texte

Je peux le reproduire sur Linux, mais pas sur Windows ou Mac.

Le pressentiment ci-dessus est correct; Linux inclut également les données sous forme d'URL et de texte en clair.

fichiers déposés à data: prototcol data URI chez firefox, et chrome, chrome

data:text/html,<textarea></textarea>

Firefox

Le nom de chemin complet du fichier ou du dossier défini comme .value De <textarea>.

Chrome, chrome

Déposer un fichier dans data URI N'ayant que textarea élément dans chrome, chrome remplace le data URI Par un chemin de fichier supprimé dans la barre d'adresse et charge le fichier déposé dans le même onglet, en remplaçant le data URI Avec le contenu du fichier déposé.

plnkr http://plnkr.co/edit/ZfAGEAiyLLq8rGXD2ShE?p=preview


html, javascript pour reproduire le problème décrit ci-dessus

<!DOCTYPE html>
<html>

<head>
  <style>
    body {
      height: 400px;
    }

    textarea {
      width: 95%;
      height: inherit;
    }
  </style>

  <script>
    window.onload = function() {
      var button = document.querySelector("#myfile + button");
      var input = document.getElementById("myfile");
      var display = document.getElementById("DisplayText");
      var text = null;

      function readFullPathToFileOnUserFileSystem(e) {
        var path = e.target.value;
        console.log(path);
        var w = window.open(path, "_blank");
        var img = new Image;
        img.crossOrigin = "anonymous";
        img.onload = function() {
          document.body.appendChild(this);
        }
        img.onerror = function(err) {
          console.log("img error", err.message)
        }
        img.src = path;
        var request = new XMLHttpRequest();
        request.open("GET", path.trim(), true);
        request.onload = function() {
          console.log(this.responseText)
        }
        request.error = function(err) {
          console.log(err.message)
        }
        request.send();

      }

      display.addEventListener("input", readFullPathToFileOnUserFileSystem);
      input.addEventListener("change", addDoc);
      input.addEventListener("progress", function(event) {
        console.log("progress", event)
      });
      button.addEventListener("click", handleText)

      function addDoc(event) {
        var mozResult = [];

        function mozReadDirectories(entries, path) {
          console.log("dir", entries, path);
          return [].reduce.call(entries, function(promise, entry) {
              return promise.then(function() {
                console.log("entry", entry);
                return Promise.resolve(entry.getFilesAndDirectories() || entry)
                  .then(function(dir) {
                    console.log("dir getFilesAndDirectories", dir)
                    return dir
                  })
              })
            }, Promise.resolve())
            .catch(function(err) {
              console.log(err, err.message)
            })
            .then(function(items) {
              console.log("items", items);
              var dir = items.filter(function(folder) {
                return folder instanceof Directory
              });
              var files = items.filter(function(file) {
                return file instanceof File
              });
              if (files.length) {
                console.log("files:", files, path);
                mozResult = mozResult.concat.apply(mozResult, files);

              }
              if (dir.length) {
                console.log(dir, dir[0] instanceof Directory, dir[0]);
                return mozReadDirectories(dir, dir[0].path || path);

              } else {
                if (!dir.length) {

                  return Promise.resolve(mozResult).then(function(complete) {
                    return complete
                  })

                }
              }

            })
            .catch(function(err) {
              console.log(err)
            })

        };

        console.log("files", event.target.files);
        if ("getFilesAndDirectories" in event.target) {
          return (event.type === "drop" ? event.dataTransfer : event.target)
          .getFilesAndDirectories()
            .then(function(dir) {
              if (dir[0] instanceof Directory) {
                console.log(dir)
                return mozReadDirectories(dir, dir[0].path || path)
                  .then(function(complete) {
                    console.log("complete:", complete);
                    event.target.value = null;
                  });
              } else {
                if (dir[0] instanceof File && dir[0].size > 0) {
                  return Promise.resolve(dir)
                    .then(function(complete) {
                      console.log("complete:", complete);
                    })
                } else {
                  if (dir[0].size == 0) {
                    throw new Error("could not process '" + dir[0].name + "' directory" + " at drop event at firefox, upload folders at 'Choose folder...' input");
                  }
                }
              }
            }).catch(function(err) {
              console.log(err)
            })
        }

        var reader = new FileReader();
        reader.onload = function(e) {
          text = reader.result;
          console.log("FileReader.result", text);
          button.removeAttribute("disabled");
        }

        reader.onerror = function(err) {
          console.log(err, err.loaded, err.loaded === 0, file);
          button.removeAttribute("disabled");
        }

        reader.onprogress = function(e) {
          console.log(e, e.lengthComputable, e.loaded, e.total);
        }

        reader.readAsArrayBuffer(file);

      }

      function handleText() {

        // do stuff with `text`: `reader.result` from `addDoc`
        display.textContent = text;
        button.setAttribute("disabled", "disabled");
        // set `text` to `null` if not needed or referenced again
        text = null;
      }
    }
  </script>
</head>

<body>
  <input type="file" id="myfile" webkitdirectory directory allowdirs>
  <button type="button" disabled>Add Document</button>
  <br>
  <br>
  <textarea id="DisplayText"></textarea>
</body>

</html>

plnkr http://plnkr.co/edit/8Ovw3IlYKI8BYsLhzV88?p=preview


Vous pouvez utiliser l'événement change attaché à l'élément #myfile Pour gérer l'action de sélection de fichier par l'utilisateur.

Substituez l'élément <textarea> À l'élément <p> Pour afficher le résultat de l'événement load à partir de l'appel .readAsText().

Pour afficher .result De FileReader à click à l'élément button, définissez la variable text sur reader.result Dans load événement de FileReader à click événement à button définissez .textContent De l'élément #DisplayText Sur la variable référençant précédemment reader.result.

<!DOCTYPE html>
<html>
  <style>
    body {
      height: 400px;
    }
    textarea {
      width:95%;
      height: inherit;
    }
  </style>
<head>
  <script>
    window.onload = function() {
        var button = document.querySelector("#myfile + button");
        var input = document.getElementById("myfile");
        var display = document.getElementById("DisplayText");
        var text = null;
        input.addEventListener("change", addDoc);
        button.addEventListener("click", handleText)

        function addDoc(event) {
          var file = this.files[0]
          var reader = new FileReader();      
          reader.onload = function(e) {
            text = reader.result;
            button.removeAttribute("disabled");
          }

          reader.onerror = function(err) {
            console.log(err, err.loaded
                        , err.loaded === 0
                        , file);
            button.removeAttribute("disabled");
          }

          reader.readAsText(event.target.files[0]);
        }

        function handleText() {
          
          // do stuff with `text`: `reader.result` from `addDoc`
          display.textContent = text;
          button.setAttribute("disabled", "disabled");
          // set `text` to `null` if not needed or referenced again
          text = null; 
        }
    }
  </script>
</head>

<body>
  <input type="file" id="myfile" accept="text/*">
  <button type="button" disabled>Add Document</button><br><br>
  <textarea id="DisplayText"></textarea>
</body>

</html>
13
guest271314

L'objet FileReader permet aux applications Web de lire de manière asynchrone le contenu des fichiers (ou des tampons de données brutes) stockés sur l'ordinateur de l'utilisateur, à l'aide d'objets File ou Blob pour spécifier le fichier ou les données à lire.

Les objets fichier peuvent être obtenus à partir d'un objet FileList renvoyé à la suite de la sélection par un utilisateur de fichiers à l'aide de l'élément, à partir de l'objet DataTransfer d'une opération de glisser-déposer, ou à partir de l'API mozGetAsFile () sur un HTMLCanvasElement.

La méthode readAsText est utilisée pour lire le contenu du Blob ou du fichier spécifié. Lorsque l'opération de lecture est terminée, le readyState est changé en DONE, le loadend est déclenché et l'attribut result contient le contenu du fichier sous forme de chaîne de texte.

Syntaxe

instanceOfFileReader.readAsText(blob[, encoding]);

Paramètres

Blob

Objet blob ou fichier à lire.

encodage Facultatif

Une chaîne spécifiant l'encodage à utiliser pour les données renvoyées. Par défaut, UTF-8 est supposé si ce paramètre n'est pas spécifié.

Pour les métadonnées sur un fichier, nous pouvons vérifier l'objet File F de telle sorte que: F a un état de lisibilité OPENED. F fait référence à la séquence d'octets. F.size est défini sur le nombre total d'octets en octets. F.name est défini sur n. F.type est défini sur t.

Remarque: Le type t d'un fichier est considéré comme un type MIME analysable si la chaîne codée ASCII représentant le type de l'objet fichier, lorsqu'elle est convertie en une séquence d'octets, ne retourne pas indéfinie pour l'algorithme de type d'analyse MIME [MIMESNIFF].

F.lastModified est défini sur d.

Voir plus sur la compatibilité du navigateur et le document détaillé pour FileReader , File et readAsText sur MDN, également ceci W3C draft for FileApi =

1
Samdeesh