好的,您想“记住并重新填充文件输入”、“记住他们的选择并重新显示文件输入,并在重新加载时预先选择文件页面的“..
在对我之前的回答的评论中,您声明您并没有真正接受替代方案:“抱歉,没有 Flash 和 Applets,只有 javscript 和/或文件输入,可能是拖放。”
我在浏览(相当多)重复问题时注意到(1、2、3 等),几乎所有其他答案都类似于:“不你不能,那将是一个安全问题”,可以选择后跟一个简单的概念或代码示例,概述安全风险。
但是,像骡子一样固执的人(在一定程度上不一定是坏事)可能会认为这些答案是:“不,因为我说过”,这确实是不同的东西:“不,这里是不允许它的规格”。
所以这是我第三次也是最后一次尝试回答你的问题(我引导你到水坑,我带你到河边,现在我把你推到源头,但我不能让你喝水)。
编辑 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?