好的,您想“记住并重新填充文件输入”,“记住他们的选择并使用重新加载页面时预先选择的文件重新显示文件输入”..
并且在对我之前的答案的评论中,您声明您是对替代方案并不真正开放:“抱歉,没有 Flash 和小程序,只有 javscript 和/或文件输入,可能会拖放。”
我注意到在浏览(一段)重复的问题(1,2,3等),几乎所有其他的答案是沿着线:“不,你不能,那将是一个安全问题”,然后任选通过一个简单的概念或代码示例概述安全风险。
然而,像骡子一样固执的人(在一定程度上不一定是坏事)可能会认为这些答案是:“不,因为我这么说”,这确实是不同的:“不,这里是规格禁止它”。
所以这是我第三次也是最后一次回答你的问题(我带你到水坑,我带你到河边,现在我推你到源头,但我不能让你喝)。
编辑3:
您想要做的实际上曾经在RFC1867第 3.4 节中描述/“建议” :
VALUE 属性可能与<INPUT TYPE=file>
默认文件名的标签一起使用。这种用途可能与平台有关。然而,在不止一个事务的序列中,这可能是有用的,例如,避免让用户一遍又一遍地提示输入相同的文件名。
事实上,HTML 4.01 规范第 17.4.1 节规定:
用户代理可以使用 value 属性的值作为初始文件名。
(“用户代理”是指“浏览器”)。
鉴于javascript可以修改和提交表单(包括文件输入)并且可以使用css隐藏表单/表单元素(如文件输入)的事实,仅上述语句就可以静默上传在用户无意/不知情的情况下从用户的计算机中获取文件。
显然,这是不可能的,这一点非常重要,因此,(以上)RFC1867 在第 8 节安全注意事项中指出:
重要的是,用户代理不发送用户未明确要求发送的任何文件。因此,HTML 解释代理应确认任何可能以<INPUT TYPE=file VALUE="yyyy">
.
但是,唯一实现此功能的浏览器(我知道)是(一些旧版本)Opera:它接受<input type="file" value="C:\foo\bar.txt>
由 javascript ( elm_input_file.value='c:\\foo\\bar.txt';
)设置的or 值。
当这个文件框在表单提交时没有改变时,Opera 会弹出一个安全窗口,通知用户哪些文件将被上传到什么位置(url/网络服务器)。
现在,人们可能会认为,所有其他的浏览器分别对违反规范的,但是这将是错误的:因为该规范指出:“ may
”(它并没有说“ must
‘)’..使用value属性为初始文件名”。
而且,如果浏览器不接受设置文件输入值(也就是,将该值设为“只读”),那么浏览器也不需要弹出这样一个“可怕”和“困难”的安全性-pop-up(如果用户不理解它(和/或“有条件”总是单击“确定”),它甚至可能无法达到其目的)。
然后让我们快进到 HTML 5..
在这里所有这些歧义都被清除了(但它仍然需要一些令人费解):
在4.10.7.1.18 文件上传状态下,我们可以在簿记详细信息中阅读:
- 值 IDL 属性处于模式文件名。
...
- 必须省略元素的 value 属性。
因此,必须省略文件输入的 value 属性,但它也以某种称为“文件名”的“模式”运行,如4.10.7.4 通用输入元素 API 中所述:
value IDL 属性允许脚本操作输入元素的值。该属性处于以下模式之一,用于定义其行为:
跳到这个“模式文件名”:
获取时,它必须返回字符串“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 选项,称为“上传文件时包括本地目录路径”,对于一般 Internet 区域设置为“禁用”(在一般 Internet 区域中设置为“启用”)。受信任区域)默认情况下。
此更改造成了一个小破坏(主要是在“为 IE 优化”环境中),其中各种自定义代码和专有“控件”都无法获取上传文件的文件名:它们被硬编码为期望包含一个完整的路径并提取最后一个反斜杠之后的部分(如果幸运的话,或者正斜杠......)。1 , 2
随之而来的是 HTML5,
正如您在上面阅读的那样,“模式文件名”指定:
获取时,它必须返回字符串“C:\fakepath\”,后跟所选文件列表中第一个文件的文件名(如果有),或者如果列表为空,则返回空字符串。
他们注意到
这种“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\
当获取文件输入的文件名值,
最重要的是,他们将只返回第一个(从“选定的文件列表”),文件名应的file-input 接受多个文件并且用户选择了多个文件。
因此,在最近的过去,目前以及在可预见的 HTML5 未来,人们通常只会获得文件名。
这将我们带到了我们需要检查的最后一件事:这个“选定文件列表”/多个文件,这将我们带到了谜题的第三部分:
(HTML5) 文件 API
首先:“文件 API”不应与“文件系统 API ”混淆,这里是文件系统 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
让我们继续“文件 API ”,
它的抽象告诉我们:
该规范提供了一个 API,用于在 Web 应用程序中表示文件对象,以及以编程方式选择它们并访问它们的数据。这包括:
- FileList 接口,表示从底层系统中单独选择的文件的数组。用于选择的用户界面可以通过 调用
<input type="file">
,即当输入元素处于文件上传状态 [HTML] 时。
- Blob 接口,表示不可变的原始二进制数据,并允许将 Blob 对象内的字节范围作为单独的 Blob 进行访问。
- 一个 File 接口,其中包含有关文件的只读信息属性,例如文件的名称和上次修改(在磁盘上)的日期。
- FileReader 接口,提供读取文件或 Blob 的方法,以及获取这些读取结果的事件模型。
- 用于二进制数据(例如文件)的 URL 方案,以便它们可以在 Web 应用程序中引用。
因此,FileList
可以通过输入字段在文件模式来填充:<input type="file">
。
这意味着以上所有关于 value-attribute 的内容仍然适用!
当输入字段处于文件模式时,它会获得一个只读属性files
,该属性类似于数组FileList object
,引用输入元素的用户选择的文件,并且FileList interface
.
我是否提到该类型的 -files
属性FileList
是只读的 (文件 API 部分 5.2)?:
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
,它是一个类似数组的属性,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
是选择的第一个文件的文件名
(这是value
从输入类型=“文件”返回的)。
这个“用于二进制数据(例如文件,以便它们可以在 Web 应用程序中被引用)的 URL 方案”怎么样,它肯定可以保存对用户选择的文件的私有引用?
从文件 API - Blob 和文件参考的 URL,我们可以了解到:
该规范定义了一个包含以下 URL 的方案:
blob:550e8400-e29b-41d4-a716-446655440000#aboutABBA。
这些都存储在一个URL store
(浏览器甚至应该有自己的迷你 HTTP 服务器,这样人们就可以在 css、img src 甚至 XMLHttpRequest 中使用这些 url。
可以使用以下方法创建这些Blob URL
s:
var myBlobURL=window.URL.createFor(object);
返回Blob URL
在第一次使用后自动撤销的a 。
var myBlobURL=window.URL.createObjectURL(object, flag_oneTimeOnly);
返回一个可重用的Blob URL
(除非 flag_oneTImeOnly 评估为真)并且可以被撤销window.URL.revokeObjectURL(myBlobURL)
。
宾果游戏你可能会想……然而……URL Store
它只在会话期间维护(所以它会在页面刷新时继续存在,因为它仍然是同一个会话)并且在文档被卸载时丢失。
来自MDN - 使用对象 URL:
对象 URL 是标识 File 对象的字符串。每次调用 window.URL.createObjectURL() 时,都会创建一个唯一的对象 URL,即使您已经为该文件创建了对象 URL。每一个都必须被释放。虽然它们在文档卸载时自动释放,但如果您的页面动态使用它们,您应该通过调用 window.URL.revokeObjectURL() 显式释放它们
这意味着,即使您将Blob URL
字符串存储在 cookie 或持久性本地存储中,该字符串在新会话中也无用!
这应该让我们回到一个完整的循环和最终结论:
不可能(重新)填充输入字段或用户选择的文件(不在浏览器沙盒“本地存储”区域中)。
(除非你强迫你的用户使用过时的 Opera 版本,或者强迫你的用户使用 IE 和一些 activeX 编码/module(实现自定义文件选择器)等)
进一步阅读:
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 章: filesystem api
如何保存 window.URL.createObjectURL() 结果以备将来使用?
Blob 持续多久?
如何解决 C:\fakepath?