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

#javascript #html #drag-and-drop #textarea #fileapi

#javascript #HTML #перетаскивание #текстовая область #fileapi

Вопрос:

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

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

Следующий код JavaScript, который был сопоставлен с ним, выглядит следующим образом:

 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>*/
}
  

После просмотра файла из локальной системы я попытался удалить «просмотренный» документ из папки перед нажатием на addDoc() . После нажатия кнопки я все еще мог видеть, что Filereader.result не равно нулю, и мог отображать все содержимое.

Может кто-нибудь объяснить, как работает Filereader? Связано ли это с тем, что FileReader привязывается при просмотре файла?

Также можем ли мы проверить, похож ли системный атрибут только для чтения с FileReader на Java File.canread() ?

Может кто-нибудь предложить по этому поводу? У меня есть IE11 для тестирования кода.

Ответ №1:

FileReader load событие устанавливает .result значение асинхронно. Для доступа к событию .result use load или loadend .

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

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

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

2.9.8 Обезьяний патч для объектов Blob и FileList

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

Blob объекты являются cloneable objects .

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

  2. Если это так closed , то бросьте "DataCloneError" DOMException .

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

FileList объекты являются клонируемыми объектами.

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

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

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

Возвращает вывод.


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

At chrome, chromium if read-only permission is set for file at local filesystem and user selects file at <input type="file"> element, where FileReader is used to read file, an error is thrown at FileReader , generated from FileReader progress event.

If a Blob URL is set to the same file object, the blob: URL will not return the the read-only file at request to the Blob URL .

Selection of folder where folder permission is set to read-only

Chrome, chromium

At chrome, chromium where webkitdirectory attribute is set and folder is selected with read-only permission FileList .length of event.target.files returned 0 ; event.target.files.webkitGetAsEntry() is not called, "No file chosen" is rendered at <input type="file"> shadowDOM . When a folder is dropped at <input type="file"> or element where droppable attribute set, the directory .name and .path of the read-only folder is displayed at drop event.dataTransfer .

When user drops file or folder at <textarea> element, where no drop event is attached beforeunload event is called and a prompr is displayed at UI

 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

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

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

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

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

Если в <textarea> элементе выбрано и удалено несколько файлов, все полные пути к файлам задаются как .value of <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

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

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

имя файла

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

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

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

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

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

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

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

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

Сообщение об ошибке https://bugzilla.mozilla.org/show_bug.cgi?id=1311823


Удаление файла в <textarea> в URI данных

Следующий комментарий Нила Дикина к сообщению об ошибке

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

  1. Открытые данные: текст / html,
  2. Перетащите файл с рабочего стола в текстовую область

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

Приведенная выше догадка верна; Linux также включает данные в виде URL и открытого текста.

dropped files at data: prototcol data URI at firefox, and chrome, chromium

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

Firefox

The full path name of file or folder set as .value of <textarea> .

Chrome, chromium

Dropping file at data URI having only textarea element at chrome, chromium replaces the data URI with dropped file path at address bar, and loads the dropped file at the same tab, replacing the data URI with the content of the dropped file.

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


html , javascript to reproduce issue described above

 <!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 amp;amp; 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 at button , установите переменную text в reader.result значение load в FileReader событии click at button event при .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>  

Комментарии:

1. @sushmithaP Смотрите 2.9.8 Monkey patch для объектов Blob и FileList , File API

2. Спасибо за ответ. Но в дополнение к этому, как удаление файла в локальной файловой системе после просмотра и перед adddoc() не влияет на reader.result. Создает ли это какой-либо кеш?

3.@sushmithaP В html <input type="file"> элементе id есть "myfile" , на javascript который вы ссылаетесь #myFile , который не отображается в html вопросе. Кроме того, используйте change событие, прикрепленное к <input type="file"> элементу, для обработки файла, выбранного из локальной файловой системы.

4. @sushmithaP обнаружил ошибку в Firefox, из-за которой файлы удалялись из <textarea> наборов элементов .value <textarea> для заполнения пути к файлу в локальной файловой системе.

Ответ №2:

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

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

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

Синтаксис

 instanceOfFileReader.readAsText(blob[, encoding]);
  

Параметры

Большой двоичный объект

Большой двоичный объект или файл, из которого нужно читать.

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

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

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

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

F.lastModified имеет значение d.

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

Комментарии:

1. Как вы отвечаете на адресные вопросы, представленные в OP «Происходит ли так, что программа чтения файлов привязывается при просмотре файла? Также можем ли мы проверить, похож ли системный атрибут FileReader только для чтения с FileReader на Java File.canread()?» ?

2. добавлены все доступные атрибуты файла, как указано в w3c, но по-прежнему доступно только для чтения

3. Вы пробовали читать файл или папку, для которых в файловой системе установлено разрешение только для чтения? «добавлены все доступные атрибуты файла, как указано в w3c, но по-прежнему доступно только для чтения» Это часть вопроса. Ваш ответ не касается этой части вопроса или этих вопросов «После просмотра файла из локальной системы я попытался удалить «просмотренный» документ из папки, прежде чем нажать addDoc(). После нажатия кнопки я все еще мог видеть, что Filereader.result не равен нулю и может отображать все содержимое.» .

4. @guest271314 Я пробовал файлы с разрешением только для чтения, проверьте демонстрацию woking на html5rocks.com/en/tutorials/file/dndfiles . В этой демонстрации показаны все доступные параметры и атрибуты из файла. Я смог не только читать, но и разделять файлы «Только для чтения». Файлы фактически реплицируются во временном каталоге браузера перед загрузкой в виде данных формы из нескольких частей, и вам разрешено читать файлы «Только для чтения», мы не можем просто перезаписать или изменить их

5. «Я пробовал файлы с разрешением только для чтения, проверьте демонстрацию woking на html5rocks.com/en/tutorials/file/dndfiles . Эта демонстрация показывает все доступные параметры и атрибуты из файла. Я смог не только прочитать, но и разделить файлы «Только для чтения». Файлы фактически реплицируются во временном каталоге браузера перед загрузкой в виде данных формы из нескольких частей, и вам разрешено читать файлы «Только для чтения», мы не можем просто перезаписать или изменить их » Интересно. Подумайте о том, чтобы поделиться тем, что вы нашли, с описанием того, как воспроизвести на bugzilla.mozilla.org/show_bug.cgi?id=1311823

Ответ №3:

Используйте это вместо:-

 function loadFileAsText()
{
    var fileToLoad = document.getElementById("fileToLoad").files[0];
 
    var fileReader = new FileReader();
    fileReader.onload = function(fileLoadedEvent) 
    {
        var textFromFileLoaded = fileLoadedEvent.target.result;
        document.getElementById("inputTextToSave").innerText = textFromFileLoaded;
    };
    fileReader.readAsText(fileToLoad, "UTF-8");
}  
         <p>Select a File to Load:</p>
        <input type="file" id="fileToLoad"><button onclick="loadFileAsText()">Load Selected File</button>
            <br>
            <br>
            <br>
            <p>Text file loaded:</p>
         <p id="inputTextToSave"></p>