繁体   English   中英

HTML5 File API 中的 FileReader.readAsText 如何工作?

[英]How FileReader.readAsText in HTML5 File API works?

我编写了以下代码来使用 HTML5 文件 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是否在浏览文件后立即绑定?

也可以用类似于Java File.canread() FileReader来检查系统的Readonly属性吗?

有人可以对此提出建议吗? 我有 IE11 来测试代码。

FileReader load事件异步设置.result值。 要访问.result使用loadloadend事件。

当在<input type="file"> Choose FileBrowse... UI 中Choose File ,在本地文件系统中删除文件不应影响.files调用返回的FileListFile对象。 2.9.2。 可传输对象6.7.3 DataTransfer 接口

4. Blob 接口和二进制数据

每个Blob必须有一个内部快照 state ,它必须最初设置为底层存储的状态(如果存在任何此类底层存储),并且必须通过structured clone保存。 可以为File找到snapshot state进一步规范定义。

2.9.8 Blob 和 FileList 对象的 Monkey 补丁

这个猴子补丁将在适当的时候删除。 请参阅w3c/FileAPI 问题 32

Blob对象是可cloneable objects

  1. 每个Blob对象的 [[ Clone ]] 内部方法,给定 targetRealm 并忽略内存,必须运行以下步骤:

  2. 如果这是closed ,则抛出一个"DataCloneError" DOMException

targetRealm 中返回this 的一个新实例,对应于相同的底层数据。

FileList对象是可克隆的对象

每个FileList对象的[[Clone]]内部方法,给定targetRealmmemory ,必须运行以下步骤:

  1. output成为targetRealm 中的一个新FileList对象。

  2. 对于图中的每个文件,添加? [StructuredClone][15](_file, targetRealm, memory_)输出File对象列表的末尾。

返回输出


在 webkit 和 firefox 浏览器中选择只读文件或文件夹

在 chrome 中,如果在本地文件系统中为文件设置了只读权限,并且用户在<input type="file">元素处选择文件,其中FileReader用于读取文件,则会在FileReader处引发错误,由FileReader progress生成事件。

如果Blob URL设置为相同的文件对象,则blob: URL 不会在请求时将只读文件返回给Blob URL

选择文件夹权限设置为只读的文件夹

铬、铬

在 chrome 中,设置了webkitdirectory属性并选择具有只读权限的文件夹FileList .length of event.target.files返回0 event.target.files.webkitGetAsEntry()未被调用,在<input type="file"> shadowDOM处呈现"No file chosen""No file chosen" <input type="file"> 当文件夹放置在<input type="file">或放置droppable属性的元素处时,只读文件夹的目录.name.path显示在drop event.dataTransfer

当用户删除文件或文件夹在<textarea>元素,在没有drop附着事件beforeunload事件被称为和prompr在UI显示

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

火狐

在火狐版本47.0b9与allowdirs属性被设置在<input type="file">元素,其中用户点击"Choose folder.." <input> ,该文件夹.name.path的父文件夹是在可访问的.then()链接到event.target.getFilesAndDirectories() 递归迭代Directory条目时,不会返回所选文件夹中包含的文件或文件夹; 返回一个空字符串。

如果用户单击"Choose file..." <input>并且选择了一个没有设置只读权限的文件夹,则单击文件管理器中的文件夹时,将列出该文件夹中的文件。

如果选择了设置只读权限的文件夹,则会在 UI 显示时呈现alert()通知

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

错误,安全问题

*nix 操作系统

当用户在<textarea>元素放置文件夹时,没有drop事件附加,用户文件系统file:协议中file:夹的完整路径被公开。 文件夹中包含的文件的路径也没有设置为.value 例如,

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

当一个文件在丢弃<textarea>元素,其中不drop被附加事件,在用户文件系统中的文件的完整路径被设定为.value<textarea> ; 也就是说,

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

如果在<textarea>元素中选择并放置多个文件,则所有完整文件路径都设置为<textarea> .value ,以换行符分隔\\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. 常见的<input>元素 API

文件名

获取时,它必须返回字符串 "C:\\fakepath\\" 后跟selected files列表中第一个文件的名称(如果有),或者如果列表为空,则返回空字符串。 设置时,如果新值是空字符串,则必须清空selected files列表; 否则,它必须抛出“ InvalidStateErrorDOMException

注意:这个“fakepath”要求是历史上的一个可悲的意外。 有关更多信息,请参阅文件上传状态部分中的示例。

注意:由于selected files列表中的文件名中不允许使用path components因此不能将“ \\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


在数据 URI 的 <textarea> 处删除文件

遵循Neal Deakin在错误报告中的评论

我认为提到的步骤是:

  1. 打开数据:文本/html,
  2. 从桌面拖一个文件到textarea

我可以在 Linux 上重现这个,但不能在 Windows 或 Mac 上重现。

上面的预感是正确的; Linux 也将数据作为 url 和纯文本包含在内。

data:删除文件data: firefox 和 chrome、chrome 上的协议data URI

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

火狐

设置为<textarea> .value的文件或文件夹的完整路径名。

铬、铬

在 chrome 上只有textarea元素的data URI处删除文件,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


您可以使用附加到#myfile元素的change事件来处理用户的文件选择操作。

<textarea>元素替换为<p>元素以显示来自.readAsText()调用的load事件的结果。

要显示.resultFileReaderclickbutton元件,组可变textreader.resultload的事件FileReaderclick事件在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>

FileReader 对象允许 Web 应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用 File 或 Blob 对象指定要读取的文件或数据。

文件对象可以从作为用户使用该元素选择文件的结果返回的 FileList 对象、从拖放操作的 DataTransfer 对象或从 HTMLCanvasElement 上的 mozGetAsFile() API 获得。

readAsText方法用于读取指定 Blob 或 File 的内容。 当读操作完成时,readyState 变为 DONE,loadend 被触发,result 属性包含文件内容作为文本字符串。

语法

instanceOfFileReader.readAsText(blob[, encoding]);

参数

斑点

要从中读取的 Blob 或文件。

编码可选

一个字符串,指定用于返回数据的编码。 默认情况下,如果未指定此参数,则假定为 UTF-8。

对于有关文件的元数据,我们可以检查 File 对象F ,以便: F 的可读性状态为 OPENED。 F 指的是 bytes 字节序列。 F.size设置为以字节为单位的总字节数。 F.name设置为 n。 F.type设置为 t。

注意:如果表示 File 对象类型的 ASCII 编码字符串在转换为字节序列时不会为解析 MIME 类型算法 [MIMESNIFF] 返回 undefined,则文件的类型 t 被视为可解析 MIME 类型。

F.lastModified设置为 d。

在 MDN 上查看更多关于浏览器兼容性和FileReaderFilereadAsText 的详细文档,以及FileApi 的这个W3C 草案

改用这个:-

 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>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM