【问题标题】:Remember and Repopulate File Input [duplicate]记住并重新填充文件输入[重复]
【发布时间】:2013-12-30 12:30:57
【问题描述】:

注意:

下面的答案反映了 2009 年旧版浏览器的状态。现在您可以在 2017 年通过 JavaScript 实际设置文件输入元素的值。

有关详细信息以及演示,请参阅此问题中的答案:
How to set file input value programatically (i.e.: when drag-dropping files)?

我有一个网站,允许用户多次上传文件进行处理。目前我只有一个文件输入,但我希望能够记住用户的选择并将其显示在屏幕上。

我想知道如何做的是在用户选择一个文件后,我会记住他们的选择,并在重新加载页面时使用预先选择的文件重新显示文件输入。我只需要知道如何记住和重新填充文件输入。

我也对不使用文件输入的方法持开放态度(如果可能的话)。

我正在使用 JQuery

【问题讨论】:

  • 这样就可以“记住”用户到 uhm 的路径.. 注册表、税收计划的默认位置等。好主意,谢天谢地,这已经不可能了。
  • 记住这是一个他们已经在文件输入中选择的文件。我不是在谈论访问文件系统上的任何文件。
  • 是的,你只是没有意识到。您明确要求:“在重新加载页面时重新显示 file input 和文件 pre-selected”。如果你能做到这一点,那么即使使用简单的 css,任何人都可以欺骗用户上传他们不知道的东西!此外,完整路径是私有的(假设有人可以读取您的用户名.. 或您的用户文件夹的路径),并且在现代浏览器中不应被 javascript 访问。你唯一能做的就是只记住文件名..因此毫无用处。
  • 实际上,即使您的问题的标题也非常很清楚:“记住并重新填充文件输入”......这里没有误解。没有路径的文件名是没有用的(你不能上传/访问文件),没有人有权知道我的文件夹结构的任何事情,所以你没有路径.如果您对我的文件路径没有任何权利..那么甚至无法重新填充文件输入(即使可以这样做,它也不知道应该从哪个路径获取文件)等等.等等

标签: javascript html file-upload fileapi


【解决方案1】:

好的,您想“记住并重新填充文件输入”、“记住他们的选择并重新显示文件输入,并在重新加载时预先选择文件页面的“..
在对我之前的回答的评论中,您声明您并没有真正接受替代方案:“抱歉,没有 Flash 和 Applets,只有 javscript 和/或文件输入,可能是拖放。”

我在浏览(相当多)重复问题时注意到123 等),几乎所有其他答案都类似于:“不你不能,那将是一个安全问题”,可以选择后跟一个简单的概念或代码示例,概述安全风险。

但是,像骡子一样固执的人(在一定程度上不一定是坏事)可能会认为这些答案是:“不,因为我说过”,这确实是不同的东西:“不,这里是不允许它的规格”。
所以这是我第三次也是最后一次尝试回答你的问题(我引导你到水坑,我带你到河边,现在我把你推到源头,但我不能让你喝水)。

编辑 3:

您想要做的实际上曾经在RFC1867 第 3.4 节中描述/“建议”:

VALUE 属性可以与<INPUT TYPE=file> 标记一起使用 默认文件名。这种使用可能取决于平台。它可能 然而,在不止一个交易的序列中是有用的,例如, 避免让用户一遍又一遍地提示输入相同的文件名 再次。

确实,HTML 4.01 spec section 17.4.1 指定:

用户代理可以使用 value 属性的值作为初始文件名。

(“用户代理”是指“浏览器”)。

鉴于 javascript 可以修改和提交表单(包括文件输入)并且可以使用 css 隐藏表单/表单元素(如文件输入),仅上述语句就可以实现在用户无意/不知情的情况下从用户的计算机上静默上传文件。
这显然是非常重要的,因此,(以上)RFC1867 在第 8 节安全考虑中声明:

用户代理不要发送用户拥有的任何文件,这一点很重要 没有明确要求发送。因此,HTML 解释代理是 预计会确认可能建议的任何默认文件名 <INPUT TYPE=file VALUE="yyyy">

但是,唯一实现此功能的浏览器(我知道)是(一些旧版本的)Opera:它接受 <input type="file" value="C:\foo\bar.txt> 或由 javascript 设置的值 (@987654352 @)。
当提交表单时此文件框未更改时,Opera 会弹出一个安全窗口,通知用户哪些文件将上传到哪个位置(url/webserver)。

现在有人可能会争辩说所有其他浏览器都违反了规范,但这是错误的:因为规范声明:“may”(它确实没有 em> say "must") ".. 使用 value 属性作为初始文件名".
而且,如果浏览器不接受设置文件输入值(也就是将该值设置为“只读”),那么浏览器也不需要弹出这样一个“可怕”和“困难”的安全性-pop-up(如果用户不理解它甚至可能无法达到目的(和/或被“条件”以始终单击“确定”)。

那么让我们快进到 HTML 5..
在这里,所有这些歧义都被清除了(但仍然需要一些令人费解的地方):

4.10.7.1.18 File Upload state下我们可以阅读簿记详情

  • 值 IDL 属性在模式文件名中。
    ...
  • 元素的 value 属性必须省略。

因此,必须省略文件输入的 value 属性,但它也可以在 4.10.7.4 Common input element APIs 中描述的称为“文件名”的某种“模式”下运行:

value IDL 属性允许脚本操作输入元素的值。 该属性处于以下模式之一,这些模式定义了它的行为:

跳到这个'mode filename':

在获取时,它必须返回字符串“C:\fakepath\”,后跟 选定文件列表中第一个文件的文件名(如果有),或 如果列表为空,则为空字符串。在设置时,如果新值是 空字符串,它必须清空选定文件的列表;否则, 它必须抛出一个 InvalidStateError 异常。

让我重复一遍:“它must 抛出一个InvalidStateError 异常”如果有人试图将一个文件输入值设置为一个非空字符串!!! (但可以通过将其值设置为空字符串来清除输入字段。)

因此,目前和可预见的 HTML5 未来(过去,除了 Opera),只有用户可以填充文件输入(通过浏览器或操作系统提供的“文件选择器”)。 无法使用 javascript 或通过设置默认值将文件输入(重新)填充到文件/目录中。

获取文件名/文件路径

现在,假设使用默认值(重新)填充文件输入并非不可能,那么显然您需要完整路径:目录 + 文件名(+ 扩展名)。

过去,一些浏览器,如(最值得注意的)IE6(直到 IE8)确实显示完整路径+文件名作为值:只是一个简单的 alert( elm_input_file.value ); 等在 javascript 和浏览器中还在表单提交时将此完整路径+文件名(+扩展名)发送到接收服务器。
注意:一些浏览器也有一个“文件或文件名”属性(通常发送到服务器)但是显然这不会包括路径..

这是一个现实的安全/隐私风险:恶意网站(所有者/利用者)可以获得用户主目录的路径(其中个人资料、帐户、cookie、注册表的用户部分、历史记录、收藏夹、桌面等位于已知的固定位置)当典型的非技术 Windows 用户将从以下位置上传他的文件时:C:\Documents and Settings\[UserName]\My Documents\My Pictures\kinky_stuff\image.ext
我什至没有谈论传输数据(甚至通过 https 进行“加密”)或“安全”存储这些数据时的风险!

因此,越来越多的替代浏览器开始遵循最古老的安全措施之一:在需要知道的基础上共享信息。
而绝大多数网站不需要知道文件路径,所以他们只显示文件名(+扩展名)。

到 IE8 发布时,MS 决定跟随竞争并添加一个 URLAction 选项,称为“上传文件时包含本地目录路径”,对于一般互联网区域设置为“禁用”(并且“启用” ' 在受信任区域中)默认情况下。

此更改造成了小破坏(主要在“针对 IE 优化”环境中),其中各种自定义代码和专有“控件”都无法获取已上传文件的文件名:它们被硬编码为预期一个包含完整路径的字符串,并提取最后一个反斜杠之后的部分(如果幸运的话,也可以是正斜杠......)。 1, 2

HTML5 出现了,
正如您在上面所读到的,“模式文件名”指定:

在获取时,它必须返回字符串“C:\fakepath\”,后跟所选文件列表中第一个文件的文件名(如果有),如果列表为空,则返回空字符串。

他们注意到

这个“假路径”要求是历史的不幸事故

由于历史原因,值 IDL 属性在文件名之前加上前缀 使用字符串“C:\fakepath\”。一些遗留的用户代理实际上 包括完整路径(这是一个安全漏洞)。作为一个 结果,从值 IDL 属性中获取文件名 向后兼容的方式并非易事。以下函数 以适当兼容的方式提取文件名:

function extractFilename(path) {
  if (path.substr(0, 12) == "C:\\fakepath\\")
    return path.substr(12); // modern browser
  var x;
  x = path.lastIndexOf('/');
  if (x >= 0) // Unix-based path
    return path.substr(x+1);
  x = path.lastIndexOf('\\');
  if (x >= 0) // Windows-based path
    return path.substr(x+1);
  return path; // just the filename
}

注意:我认为这个函数很愚蠢:重点是总是有一个假的 windows-path 来解析.. 所以第一个 'if' 不仅没用,而且甚至会引发错误:想象一个用户使用从以下位置上传文件的旧浏览器:c:\fakepath\Some folder\file.ext(因为它会返回:Some folder\file.ext)...
我会简单地使用:

function extractFilename(s){ 
  // returns string containing everything from the end of the string 
  //   that is not a back/forward slash or an empty string on error
  //   so one can check if return_value===''
  return (typeof s==='string' && (s=s.match(/[^\\\/]+$/)) && s[0]) || '';
} 

(正如 HTML5 规范所明确表示的那样)。

让我们回顾一下(获取路径/文件名):

  • 较旧的浏览器(以及较新的浏览器,可以将其作为选项启用,例如 IE>=8)将显示完整的 windows/unix 路径
  • 较少的旧浏览器不会显示任何路径,仅显示文件名(+扩展名)
  • 当前/未来/HTML5 兼容的浏览器在获取文件输入的时总是会在文件名前附加字符串:c:\fakepath\ 最重要的是,如果文件输入接受多个文件并且用户选择了多个文件,它们只会返回 first 文件名(来自“选定文件列表”)。

因此,在最近、当前和可预见的 HTML5 未来,人们通常只会获得文件名。

这将我们带到了我们需要检查的最后一件事:这个“选定文件列表”/多个文件,这将我们带到了难题的第三部分:

(HTML5) 文件 API

首先:'File API'不应该和'File System API'混淆,这里是File System API的摘要:

本规范定义了一个用于导航文件系统层次结构的 API,并定义了一种方式,用户代理可以通过该方式将用户本地文件系统的沙盒部分暴露给 Web 应用程序。它建立在 [FILE-WRITER-ED] 之上,而后者又建立在 [FILE-API-ED] 之上,每个都添加了不同类型的功能。

“用户本地文件系统的沙盒部分”已经清楚地表明不能使用它来获取沙盒之外的用户文件(因此与问题无关,尽管一个 可以 将用户选择的文件复制到持久性本地存储并使用 AJAX 等重新上传该副本。在上传失败时用作“重试”。但它不会是指针到同时可能已更改的原始文件)。
更重要的是,只有 webkit(想想旧版本的 chrome)实现了这个功能,而且这个规范很可能不会继续存在,因为它is no more actively maintained, the specification is abandonned for the moment as it didn't get any significant traction

让我们继续'File API',
它的抽象告诉我们:

本规范提供了一个 API 来表示文件对象 Web 应用程序,以及以编程方式选择它们和 访问他们的数据。这包括:

  • FileList 接口,它表示从底层系统中单独选择的文件的数组。用户界面 可以通过<input type="file"> 调用选择,即当输入 元素处于文件上传状态 [HTML]。
  • 一个 Blob 接口,它表示不可变的原始二进制数据,并允许将 Blob 对象中的字节范围作为单独的 斑点。
  • 文件接口,其中包括有关文件的只读信息属性,例如文件名和最后修改日期 (在磁盘上)文件。
  • FileReader 接口,提供读取文件或 Blob 的方法,以及获取这些读取结果的事件模型。
  • 一种用于二进制数据(如文件)的 URL 方案,以便在 Web 应用程序中引用它们。

因此,FileList 可以由文件模式下的输入字段填充:<input type="file">
这意味着以上关于价值属性的所有内容仍然适用!

当输入字段处于文件模式时,它会得到一个read-only attribute files,它是一个类似数组的FileList object,它引用输入元素的用户选择的文件,并且可以由FileList interface.
我有没有提到 the files-attribute of the type FileList is read-only (File API section 5.2) ? :

HTMLInputElement 接口 [HTML] 有一个 FileList 类型的只读属性...

那么,拖放呢?

来自mdn-documentation - Selecting files using drag and drop

真正的魔力发生在 drop() 函数中:

function drop(e) {
  e.stopPropagation();
  e.preventDefault();

  var dt = e.dataTransfer;
  var files = dt.files;

  handleFiles(files);
}

在这里,我们从事件中检索 dataTransfer 字段,然后拉取 文件列表,将其传递给 handleFiles()。从此 on,无论用户是否使用输入,处理文件都是相同的 元素或拖放。

所以,(就像输入字段 type="file",)事件的 dataTransfer 属性有一个类似数组的属性 files,这是一个类似数组的 FileList object,我们刚刚了解到(以上) FileList 是只读的..

FileList 包含对用户选择(或放置在放置目标上)的文件的引用和一些属性。从File API Section 7.2 File Attributes我们可以读到:

名字

文件名;在获取时,这必须返回的名称 文件作为字符串。不同的文件名有很多变体 系统;这只是文件名,没有路径 信息。获取时,如果用户代理无法提供此信息 可用,它们必须返回空字符串。

上次修改日期

文件的最后修改日期。获取时,如果用户代理可以 使此信息可用,这必须返回一个新的 Date[HTML] 对象初始化为文件的最后修改日期。如果最后 修改日期和时间未知,属性必须返回 当前日期和时间作为 Date 对象。

还有一个size属性:

F.size与fileBits Blob参数的大小相同,必须是F的不可变原始数据。

同样,没有路径,只有只读文件名。

因此:

  • (elm_input||event.dataTransfer).files 给出 FileList 对象。
  • (elm_input||event.dataTransfer).files.length 给出文件的数量。
  • (elm_input||event.dataTransfer).files[0] 是第一个选择的文件。
  • (elm_input||event.dataTransfer).files[0].name 是第一个选择的文件的文件名
    (这是从输入 type="file" 返回的 value)。

这个“用于二进制数据(例如文件,以便它们可以在 Web 应用程序中引用)的 URL 方案”怎么样,它肯定可以保存对用户选择的文件的私有引用吗?

File API - A URL for Blob and File reference我们可以了解到:

本规范定义了一个具有以下 URL 的方案:
blob:550e8400-e29b-41d4-a716-446655440000#aboutABBA。

这些存储在URL store 中(浏览器甚至应该有自己的迷你 HTTP 服务器,因此可以在 css、img src 甚至 XMLHttpRequest 中使用这些 url。

可以通过以下方式创建Blob URLs:

  • var myBlobURL=window.URL.createFor(object); 返回一个 Blob URL,在首次使用后自动撤销。
  • var myBlobURL=window.URL.createObjectURL(object, flag_oneTimeOnly); 返回一个可重复使用的 Blob URL(除非 flag_oneTImeOnly 的计算结果为真)并且可以使用 window.URL.revokeObjectURL(myBlobURL) 撤销。

您可能会认为宾果游戏...但是...URL Store 仅在会话期间维护(因此它会在页面刷新后继续存在,因为它仍然是同一会话)并在文档卸载时丢失。

来自MDN - Using object URLs

对象 URL 是标识 File 对象的字符串。每次你 调用 window.URL.createObjectURL(),创建一个唯一的对象 URL, 即使您已经为该文件创建了对象 URL。每一个 这些必须被释放。当它们自动释放时 文档已卸载,如果您的页面动态使用它们,您应该 通过调用 window.URL.revokeObjectURL() 显式释放它们

这意味着,即使您将 Blob URL 字符串 存储在 cookie 或持久性本地存储中,该字符串在新会话中也将毫无用处!

这应该将我们带入一个完整的循环和最终结论:
不可能(重新)填充输入字段或用户选择的文件(不在浏览器将“本地存储”区域沙盒化)。
(除非您强迫您的用户使用过时版本的 Opera,或者强迫您的用户使用 IE 和一些 activeX 编码/模块(实现自定义文件选择器)等)

进一步阅读:
http://www.cs.tut.fi/~jkorpela/forms/file.html
https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
http://www.html5rocks.com/en/tutorials/file/filesystem/
http://www.html5rocks.com/en/tutorials/file/dndfiles/
http://caniuse.com/filereader
JavaScript:权威指南 - David Flanagan,第 22 章:文件系统 api
How to save the window.URL.createObjectURL() result for future use?
How long does a Blob persist?
How to resolve the C:\fakepath?

【讨论】:

  • 您好 GitaarLAB,感谢您的回答。我理解您在说什么,但您假设目录信息将存储为纯文本。如果我在 Java 中实现文件输入,我将加密目录。然后我可以序列化和反序列化对象,从而记住并重新填充信息。我也无法更改该数据(如您上面的示例)。我理解对安全性的需求,并且我认为这不会是纯文本。抱歉,没有 Flash 和 Applets,只有 javscript 和/或文件输入,可能是拖放。谢谢
  • 我从未谈论过(或“假设”)您将如何处理数据的安全性或不安全性,因此您不理解我之前的回答(s 和 cmets)您不能这样做。另外,为了确定,我想指出 javascript 与 java 无关!!! 所以我更新了我的答案 a little (我谦虚要求您不要删除您的问题,从而删除我在此原始答案中所做的所有工作。同样卑微的请求也发给了模组)。
  • 感谢您了解 HTML5 及其新的可能性(例如 File API)。还不知道这些。
  • “所以,你告诉我有机会!” forums.crackberry.com/attachments/android-f169/…
  • 好文章。我有一些评论/想法。如果我想从拖放事件文件列表中填充input 的文件列表怎么办?对于不支持将文件拖放到input 元素的浏览器中的用户体验一致性?我认为它不会带来任何安全风险(我不知道文件位置,它是用户发起的操作,等等),但我没有看到实现它的方法。
【解决方案2】:

在您的表单上创建一个输入字段。当用户选择一个文件时,将结果复制到该字段,例如:

jQuery('#inFile').change(
 function(){ jQuery('#inCopy').val( jQuery('#inFile').val() ); }
);

实际上,结果并没有完全复制,而是复制了“C:/fakepath/SELECTED_FILE_NAME”。虽然您不能设置文件输入的值,但您可以在服务器准备表单时设置文本输入字段的值,无需“C:/fakepath/”。

现在,当服务器取回表单时,检查文本输入字段。如果它以“C:/fakepath/”开头,那么用户必须选择了一个新文件,所以上传他们的新选择。如果不是,那么用户选择了之前的选择,这应该不是问题,因为根据原始问题,之前的选择已经上传并且应该(至少通过适当的编程,它可以)仍然是在服务器上。

【讨论】:

  • 这仍然不允许我保存有关文件的信息并在以后检索它。
  • 我不明白为什么不这样做。例如,mySQL 不应受到任何影响。
【解决方案3】:

冒着踩到 GitaarLAB 提供的大量信息的风险,我可能会建议 DaveWalley 非常接近为该问题提供实用的解决方案。你们俩都帮了我很大的忙,非常感谢。

我的主要信息是,

  1. 文件输入提供单向街道。它坐在那里等待处理您的下一次上传。这就是它所做的一切,差不多。它接收
  2. 文件输入标签附近的 div 或只读文本表单输入可以执行服务。即它可以用来告诉用户,“这是当前上传的内容:” - 要显示的文件名需要从您的服务器端逻辑中填充。

所以一个基本的用例是:

  • 用户通过网页表单输入上传文件
  • 服务器端逻辑存储文件
  • 在 Ajax 循环或网页重新加载中,服务器端代码标识要写入 div 的文件名字符串,“这是当前上传的内容”
  • 文件输入也可能会重新呈现,因此欢迎用户也上传另一个文件 / 代替。

您可能需要做更多的工作来处理“* required”验证,但这是另一回事。

【讨论】:

    【解决方案4】:

    如果您真正想要的只是文件数据的临时持久性,例如页面上有错误或您想要确认页面,则在将数据提交到数据库之前,这通常是完成将文件放置在临时位置并将数据保存在隐藏字段中。

    这不会重新填充文件输入字段。但也可以只取输入的文件名,放在文件输入框旁边。

    像这样:

    <input type=hidden name="filename" value="<?php echo $filename; ?>" />
    <input type="file" name="uploadfile" size="50" />
    
    <?php if (!empty($filename)) echo $filename; ?>
    

    【讨论】:

      猜你喜欢
      • 2016-01-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多