注意:
我有一个网站,允许用户多次上传文件进行处理。目前,我只有一个文件输入,但是我希望能够记住用户的选择并将其显示在屏幕上。
我想知道的是在用户选择文件后,我会记住他们的选择,并重新显示输入的文件,并在重新加载页面时预先选择该文件。我所需要知道的是如何记住并重新填充文件输入。
我也欢迎不使用文件输入的方法(如果可能的话)。
我正在使用JQuery
好的,您想要“记住并重新 填充文件输入 ”,“记住他们的选择并 重新显示 带有 重新加载页面时预先选择 的文件的文件输入_。”。 在对我之前的回答的评论中,您声明您正在没有真正开放的选择:“对不起,但没有Flash和Applet,只是javscript和/或文件输入,可能会拖放。”
但是,固执地someone子的人(一定程度上不一定是一件坏事)可能会认为这些答案是:“不,因为我是这么说的”,这与当时的确有所不同:“不,这是规格禁止”。 因此,这是我第三次也是最后一次尝试回答您的问题(我将您引导到水坑,将您带到河边,现在将您推向水源,但我不能让您喝水)。
编辑3:
您实际上想在RFC1867第3.4节中描述/“建议”您要做的事情:
VALUE属性可能与<INPUTTYPE=file>标记一起用于默认文件名。这种用法可能取决于平台。但是,在一个以上事务的序列中,这可能很有用,例如,避免让用户一遍又一遍地提示相同的文件名。
<INPUTTYPE=file>
实际上, _[HTML 4.01规范的第17.4.1节规定:
用户代理可以将value属性的值用作初始文件名。
(通过“用户代理”,它们的意思是“浏览器”)。
鉴于javascript既可以修改和提交表单(包括文件输入),又可以使用CSS隐藏表单/表单元素(例如文件输入)的事实,仅上述语句就可以实现静默上传来自用户计算机的文件,而没有他的意图/知识。 显然这是极其重要的,这是不可能的,因此,RFC1867(在上文中)在 第8节“安全注意事项”中 指出:
用户代理不要发送用户未明确要求发送的任何文件,这一点很重要。因此,HTML解释器应该确认可能会建议使用的任何默认文件名<INPUT TYPE=fileVALUE="yyyy">。
<INPUT TYPE=fileVALUE="yyyy">
但是,唯一实现该功能的浏览器(我知道)是 Opera (某些旧版本): Opera :它接受<input type="file"value="C:\foo\bar.txt>javascript(elm_input_file.value='c:\\foo\\bar.txt';)设置的或值。 如果在提交表单时此文件框未更改,Opera将弹出一个安全窗口,通知用户要将哪个文件上传到哪个位置(URL / Web服务器)。
<input type="file"value="C:\foo\bar.txt>
elm_input_file.value='c:\\foo\\bar.txt';
现在,有人可能会争辩说所有其他浏览器都违反了规范,但这是错误的:由于规范说明:“ may”(未说“must”)“ ..使用value属性作为初始文件名”。 而且,如果浏览器不接受设置文件输入值(又名该值只是“只读”),则浏览器也不需要弹出这样的“可怕”和“困难”的安全性-pop-up(如果用户不理解它(和/或被“限制为”始终单击“确定”),这甚至可能无法达到目的)。
may
must
然后,让我们快速前进到HTML5。 在这里,所有这些歧义都被清除了(但是仍然有些令人费解):
在 4.10.7.1.18文件上传状态下,我们可以阅读簿记详细信息 :
因此,必须省略文件输入的value属性,但它也可以在4.10.7.4通用输入元素API中 描述的称为“文件名”的“模式”下运行:
值IDL属性允许脚本操纵输入元素的值。该属性采用以下方式之一,以定义其行为:
跳至此“ 模式文件名 ”:
获取时,它必须返回字符串“C:\fakepath\”,后跟所选文件列表中第一个文件的文件名(如果有),或者返回空字符串(如果列表为空)。设置时,如果新值是空字符串,则必须清空所选文件的列表;否则,它必须抛出InvalidStateError异常。
让我重复一遍:如果试图将文件输入值设置为不为空的字符串,则“它将must引发InvalidStateError异常!!!” (但是可以通过将输入字段的值设置为空字符串来清除输入字段。)
因此,在当前以及可预见的HTML5未来 (以及Opera以外的过去)中, 只有用户可以填充文件输入 (通过浏览器或操作系统提供的“文件选择器”)。 不能使用javascript或通过设置默认值将文件输入(重新)填充到文件/目录中。
现在, 假设 并非没有可能用默认值(重新)填充文件输入,那么显然您需要完整的路径:directory + filename(+扩展名)。
过去,某些浏览器(例如(最著名的)IE6(最高IE8)) 的确 显示了完整路径+文件名作为值:alert( elm_input_file.value );在javascript中只是一个简单的等,浏览器也将此完整路径+文件名(+扩展名)发送给了提交表单时接收服务器。 注意:某些浏览器还具有’file或fileName’属性(通常发送到服务器),但是显然其中不包含路径。
alert( elm_input_file.value );
这是现实的安全/隐私风险:恶意网站(所有者/开发者)可能获取到用户主目录的路径(个人资料,帐户,Cookie,注册表的用户部分,历史记录,收藏夹,桌面等在其中)。位于已知的恒定位置),那么典型的非技术Windows用户将从以下位置上传文件:C:\Documentsand Settings\[UserName]\My Documents\My Pictures\kinky_stuff\image.ext。 在传输数据(甚至通过https“加密”)或“安全”存储该数据时,我什至没有谈到风险!
C:\Documentsand Settings\[UserName]\My Documents\My Pictures\kinky_stuff\image.ext
因此,越来越多的替代浏览器开始遵循最古老的,经过验证的安全措施之一:在需要了解的基础上共享信息。 而且绝大多数网站 不需要知道 文件路径,因此它们只显示文件名(+扩展名)。
到IE8发布时,MS决定参加比赛,并添加了一个URLAction选项,称为“上传文件时包含本地目录路径”,该选项在一般Internet区域中设置为“禁用”(在Internet区域中设置为“启用”)信任区域)。
这项更改造成了小麻烦(主要是在“针对IE优化”环境中),所有自定义代码和专有“控件”都无法获取已上传文件的文件名:它们被硬编码为期望包含完整路径,并在最后一个反斜杠之后提取部分(如果幸运的话,也可以提取正斜杠)。[1] HTML5伴随而来,正如您在上面所阅读的,“ mode filename”指定:
获取时,它必须返回字符串“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路径来解析。.因此,第一个’if’不仅无用,甚至会引发一个错误:想象一个用户使用一个较旧的浏览器上传来自的文件:(c:\fakepath\Some folder\file.ext因为它将返回:Some folder\file.ext)… 我将简单地使用:
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规范明确规定的那样)。
让我们回顾一下(获取路径/文件名):
c:\fakepath\
因此,在最近的过去,当前以及可预见的HTML5将来,通常只会获得文件名。
这使我们进入需要检查的最后一件事:此“选定文件列表” /多个文件,这使我们进入了难题的第三部分:
首先:“文件API”不应与“ 文件系统API ” 混淆,这是文件系统API的摘要:
该规范定义了一个用于导航文件系统层次结构的API,并定义了一种方法,用户代理可以通过该方法将用户本地文件系统的沙盒部分公开给Web应用程序。它建立在[FILE- WRITER-ED]之上,而[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
is no more actively maintained, the specification is abandonned for the moment as it didn't get any significant traction
让我们继续“ File API ”, 它抽象地告诉我们:
该规范提供了一个API,用于表示Web应用程序中的文件对象,以及以编程方式选择它们并访问其数据。这包括:
<input type="file">
因此,FileList可以由文件模式下的输入字段填充<input type="file">。 这意味着上述关于价值属性的所有内容仍然适用!
FileList
当输入字段处于文件模式时,它会获得一个只读属性files,该属性类似于数组FileListobject,它引用输入元素的用户选择的文件,并且可以通过进行访问FileListinterface。我是否提到类型的files-attribute FileList是 只读的(文件API第5.2节)?:
files
FileListobject
FileListinterface
HTMLInputElement接口[HTML]具有FileList …类型的只读属性。
好吧,拖放呢?
从mdn文档-使用拖放选择文件
真正的魔力发生在drop()函数中:
function drop(e) { e.stopPropagation(); e.preventDefault(); var dt = e.dataTransfer; var files = dt.files; handleFiles(files); }
在这里,我们从事件中检索dataTransfer字段,然后从中提取文件列表,并将其传递给handleFiles()。从现在开始,无论用户使用输入元素还是拖放操作,文件处理都是相同的。
因此,(就像输入字段type =“file”一样),该事件的dataTransfer属性具有类似数组的属性files,该属性类似于数组,FileListobject并且我们刚刚(如上所述)了解到FileList是 只读的 。
dataTransfer
FileList包含对用户选择(或放置在放置目标上)的文件的引用以及一些属性。从文件API第7.2节“文件属性”中,我们可以阅读:
名称 文件名;获取时,必须以字符串形式返回文件名。不同系统上有许多文件名变体。这只是文件名,没有路径信息。获取时,如果用户代理无法提供此信息,则必须返回空字符串。 lastModifiedDate 文件的最后修改日期。获取时,如果用户代理可以使此信息可用,则必须返回一个新的Date [HTML]对象,该对象初始化为文件的最后修改日期。如果最后修改日期和时间未知,则该属性必须将当前日期和时间作为Date对象返回。
名称
文件名;获取时,必须以字符串形式返回文件名。不同系统上有许多文件名变体。这只是文件名,没有路径信息。获取时,如果用户代理无法提供此信息,则必须返回空字符串。
lastModifiedDate
文件的最后修改日期。获取时,如果用户代理可以使此信息可用,则必须返回一个新的Date [HTML]对象,该对象初始化为文件的最后修改日期。如果最后修改日期和时间未知,则该属性必须将当前日期和时间作为Date对象返回。
并且有一个size属性:
size
F.size与fileBits Blob参数的大小相同,该参数必须是F的不可变原始数据。
同样,没有路径,只有 只读 文件名。
从而:
(elm_input||event.dataTransfer).files
(elm_input||event.dataTransfer).files.length
(elm_input||event.dataTransfer).files[0]
(elm_input||event.dataTransfer).files[0].name
value
那么,“用于二进制数据(例如文件,以便它们可以在Web应用程序中被引用)使用的URL方案”又如何呢?它肯定可以保存用户选择的文件的私有引用吗?
通过File API-Blob和File的URL参考,我们可以了解到:
本规范使用以下URL定义了一种方案: blob:550e8400-e29b-41d4-a716-446655440000#aboutABBA。
这些都存储在其中URL store(浏览器甚至应该在板上拥有自己的小型HTTP服务器,因此人们可以在CSS,img src甚至XMLHttpRequest中使用这些url。
URL store
可以使用以下方法创建这些Blob URL:
Blob URL
var myBlobURL=window.URL.createFor(object);
var myBlobURL=window.URL.createObjectURL(object, flag_oneTimeOnly);
window.URL.revokeObjectURL(myBlobURL)
Bingo,您可能会认为…但是… URL Store仅在会话期间维护(因此,它仍将在同一会话中保留,因此它将在页面刷新后继续存在),并且在卸载文档时丢失。
URL Store
从MDN-使用对象网址:
对象URL是标识File对象的字符串。每次调用window.URL.createObjectURL()时,都会创建一个唯一的对象URL,即使您已经为该文件创建了对象URL。这些都必须释放。尽管它们在文档卸载时自动释放,但是如果页面动态使用它们,则应通过调用window.URL.revokeObjectURL()显式释放它们。
这意味着,即使将Blob URL 字符串 存储在cookie或持久性本地存储中,该字符串在新会话中也将毫无用处!
这应该带给我们一个完整的圈和最后的结论: 无法(重新)填充输入字段或用户选择的文件 (该 文件 不在浏览器的“本地存储”区域中)。 (除非您强迫用户使用过时的Opera版本,或强迫用户使用IE和某些activeX编码/模块(实现自定义文件选择器)等)