Как работает FileReader.readAsText в API файлов HTML5?

Я написал следующий код, чтобы проверить, существует или нет загруженный файл, используя API файлов HTML5.

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

Ответ 1

FileReader load событие устанавливает значение .result асинхронно. Чтобы получить доступ к .result, используйте событие load или loadend.

Когда файл был выбран в <input type="file"> Choose File или Browse... пользовательском интерфейсе, удаление файла в локальной файловой системе не должно влиять на объект File на FileList, возвращаемый вызовом .files. См. 2.9.2. Переносимые объекты, 6.7.3 Интерфейс DataTransfer.

4. Интерфейс Blob и двоичные данные

Каждый Blob должен иметь внутреннее состояние моментального снимка, которое должно быть изначально установлено в состояние базового хранилища, если оно такое базовое хранилище существует и должно быть сохранено через structured clone. Дальнейшее нормативное определение snapshot state можно найти для File с.

2.9.8 Патч Monkey для объектов Blob и FileList

Этот патч обезьяны будет удален со временем. См. w3c/FileAPI вопрос 32.

Blob объекты cloneable objects.

  • Каждый объект Blob object [[Clone]] внутренний метод, заданный targetRealm и игнорирующий память, должен запускать эти шаги:

  • Если это closed, тогда выпустите "DataCloneError" DOMException.

Возвращает новый экземпляр этого в targetRealm, соответствующий те же базовые данные.

FileList объекты клонируемые объекты.

Каждый FileList объект [[Clone]] внутренний метод, данный targetRealm и память, должны выполнить следующие шаги:

  • Пусть output будет новым объектом FileList в targetRealm.

  • Для каждого файла в этом, добавьте? [StructuredClone][15](_file, targetRealm, memory_) в конец списка File объектов вывода.

Возвратный вывод.


Выбор файлов или папок только для чтения в браузерах webkit и firefox

В chrome, chromium, если разрешено только чтение для файла в локальной файловой системе, и пользователь выбирает файл в элементе <input type="file">, где FileReader используется для чтения файла, ошибка генерируется при FileReader, сгенерированной из FileReader progress событие.

Если a Blob URL установлен в один и тот же файл-объект, URL blob: не будет возвращать файл только для чтения по запросу Blob URL.

Выбор папки, в которой разрешено разрешение на доступ к папке

Chrome, хром

В chrome, chromium, где установлен атрибут webkitdirectory, и папка выбрана с разрешением только для чтения FileList .length of event.target.files return 0; event.target.files.webkitGetAsEntry() не вызывается, "No file chosen" отображается в <input type="file"> shadowDOM. Когда папка удаляется в <input type="file"> или элементе, где установлен атрибут droppable, каталог .name и .path только для чтения отображается в drop event.dataTransfer.

Когда пользователь отбрасывает файл или папку в элементе <textarea>, где не подключено событие drop beforeunload вызывается событие, а prompr отображается в интерфейсе пользователя

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

Firefox

В версии firefox 47.0b9 с атрибутом allowdirs установлено значение <input type="file">, где пользователь нажимает "Choose folder.." <input>, папки .name и .path родительской папки доступны в .then() прикован к event.target.getFilesAndDirectories(). Файлы или папки, содержащиеся в выбранной папке, не возвращаются при рекурсивном итерации записей Directory; возвращается пустая строка.

Если пользователь нажимает "Choose file..." <input> и папка выбрана без разрешения только для чтения, при нажатии папки в диспетчере файлов отображаются файлы в папке.

Если выбрана папка, где установлено разрешение только для чтения, уведомление об уведомлении alert() отображается при отображении пользовательского интерфейса

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

Ошибка, проблема с безопасностью

* nix OS

Когда пользователь отбрасывает папку в элементе <textarea>, где не подключено событие drop, отображается полный путь к папке в пользовательской файловой системе file:. Пути к файлам, содержащимся в папке, также не устанавливаются как .value; например.

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

Когда файл отбрасывается в элементе <textarea>, где не подключено событие drop, полный путь к файлу в пользовательской файловой системе устанавливается как .value из <textarea>; то есть

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

Если несколько файлов выбраны и сброшены в элементе <textarea>, все полные пути к файлу будут установлены как .value из <textarea>, обозначены символом новой строки \n

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

Где XMLHttpRequest() делается для пути к файлу, и ошибка регистрируется в console

NS_ERROR_DOM_BAD_URI: Access to restricted URI denied

При установке .src элемента <img> с .crossOrigin, установленного в "anonymous" обработчик события img error, называется

При вызове window.open() с полным набором путей при первом параметре

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

Спецификация

4.10.5.1.18. Состояние загрузки файла (type=file)

ПРИМЕР 16

По историческим причинам атрибут IDL value префикс имя файла со строкой "C:\fakepath\". Некоторые устаревшие пользовательские агенты фактически включал полный путь (который был уязвимостью безопасности). В результате этого, получив имя файла из value IDL атрибут с обратной совместимостью является нетривиальным.

4.10.5.4. Общие <input> API-интерфейсы элементов

имя файла

При получении он должен вернуть строку "C:\fakepath", а затем имя первого файла в списке selected files, если есть, или пустую строку, если список пуст. На если новое значение является пустой строкой, оно должно удалить список от selected files; в противном случае он должен выбросить "InvalidStateError" DOMException.

ПРИМЕЧАНИЕ. Это требование "фальшивого пути" - печальная случайность истории. См. Пример в разделе состояния Загрузка файлов для более подробной информации.   информация.

     

ПРИМЕЧАНИЕ: Так как path components не разрешены в именах файлов в списке selected files, "\fakepath\" не может быть ошибочно   для компонента пути.

4.10.5.1.18. Состояние загрузки файла (type=file)

Компоненты пути

Если атрибут <input> type находится в File Upload, применяются правила в этом разделе.

Элемент <input> represents список selected files, каждый файл, состоящий из имени файла, типа файла и тела файла ( содержимое файла).

Имена файлов не должны содержать path components даже в том случае, если пользователь выбрал целую иерархию каталогов или несколько файлов с одно и то же имя из разных каталогов. Компоненты пути, для целей состояния File Upload, являются те части имен файлов которые разделены символами символов U + 005C REVERSE SOLIDUS().

Отчет об ошибке https://bugzilla.mozilla.org/show_bug.cgi?id=1311823


Отбрасывание файла в <textarea> при URI данных

Следующий комментарий Neal Deakin в отчете об ошибке

Я думаю, что упомянутые шаги:

  • Откройте данные: text/html,
  • Перетащите файл с рабочего стола на текстовое поле

Я могу воспроизвести это на Linux, но не на Windows или Mac.

Догадка выше правильная; Linux включает данные в виде URL-адреса и также открытый текст.

удаленные файлы в data: prototcol data URI в firefox, а хром, хром

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

Firefox

Полное имя пути к файлу или папке, установленному как .value <textarea>.

Chrome, хром

Отбрасывая файл в data URI, имеющий только элемент textarea в chrome, хром заменяет data URI на удаленный путь файла в адресной строке и загружает выпавший файл на той же вкладке, заменяя data URI содержимым отбрасываемого файла.

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


html, javascript для воспроизведения описанной выше проблемы

<!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


Вы можете использовать событие change, присоединенное к элементу #myfile, чтобы обрабатывать действие выбора файла пользователем.

Подставить элемент <textarea> для элемента <p> для отображения результата события load из .readAsText().

Чтобы отобразить .result из FileReader в click в элементе button, установите переменную text в reader.result в load событие FileReader в click событие в button установить .textContent элемента #DisplayText к переменной, ссылающейся на ранее установленный 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>

Ответ 2

Объект FileReader позволяет веб-приложениям асинхронно считывать содержимое файлов (или буферов исходных данных), хранящихся на пользовательском компьютере, используя объекты File или Blob для указания файла или данных для чтения.

Объекты файлов могут быть получены из объекта FileList, возвращаемого в результате выбора пользователем файлов с помощью элемента, из объекта DataTransfer с перетаскиванием или из API mozGetAsFile() в HTMLCanvasElement.

Метод readAsText используется для чтения содержимого указанного Blob или File. Когда операция чтения будет завершена, readyState будет изменен на DONE, инициируется loadend, а атрибут result будет содержать содержимое файла в виде текстовой строки.

Синтаксис

instanceOfFileReader.readAsText(blob[, encoding]);

Параметры

Blob

Blob или файл, из которого следует читать.

кодировка Необязательный

Строка, определяющая кодировку, используемую для возвращаемых данных. По умолчанию UTF-8 предполагается, если этот параметр не указан.

Для метаданных о файле мы можем проверить объект File F таким образом, чтобы: F имеет состояние считывания ОТКРЫТО. F обозначает байтовую последовательность байтов. F.size устанавливается на количество байтов в байтах. F.name устанавливается в n. F.type устанавливается в t.

Примечание. Тип t файла рассматривается как разборный тип MIME, если строка, кодированная в ASCII, представляющая тип объекта File, когда преобразованный в последовательность байтов, не возвращает undefined для синтаксического анализа Алгоритм типа MIME [MIMESNIFF].

F.lastModified установлено значение d.

Подробнее о совместимости браузера и подробном документе для FileReader, File и readAsText в MDN, также этот Проект W3C для FileApi