在XHR上传中处理非ASCII文件名

时间:2013-10-08 16:54:42

标签: javascript google-chrome firefox utf-8 drag-and-drop

我有非常标准的javascript / XHR拖放文件上传代码,只是遇到了一个不幸的现实世界障碍。我的(Win7)桌面上有一个名为“TEST-é-TEST.txt”的文件。在Chrome(30.0.1599.69)中,它以UTF-8的文件名到达服务器,运行正常。在Firefox(24.0)中,当文件到达服务器时,文件名似乎被破坏了。

我不相信Firebug / Chrome可能告诉我有关编码的内容,所以我检查了请求数据包的十六进制。除了非ASCII字符在两个浏览器中的编码方式不同之外,其他所有内容都是相同的:

Chrome: C3 A9 (this is the expected UTF-8 for that character)
Firefox: EF BF BD (UTF-8 "replacement character"?!)

这是Firefox的错误吗?我尝试重命名文件,用ó替换é,而Firefox的十六进制是相同的...所以这样的一个错误实际上似乎是一个浏览器错误。 (如果Firefox混淆地发送ISO-8859-1,例如,没有触摸它,我会看到一个E9字节,我可以在服务器端处理它,但它不应该破坏它!)

无论是什么原因,我可以在客户端或服务器端做些什么来纠正这个问题?如果确实将替换字符发送到服务器,那么它似乎无法恢复,所以我几乎肯定需要在客户端进行。

是的,此代码所在的页面有charset=utf-8,Firefox确认它在View> Character Encoding下将页面视为UTF-8。

此外,如果我将文件名转储到console.log,它看起来很好 - 我猜它只是在setRequestHeader("X-File-Name",file.name)之后/之后被破坏了。

最后,似乎传递给setRequestHeader()的值应该能够使代码点达到U + 00FF,因此U + 00E9(é)和U + 00F3(ó)不应该导致问题,虽然更高的代码可能会触发SyntaxError:http://www.w3.org/TR/XMLHttpRequest2/#the-setrequestheader-method

1 个答案:

答案 0 :(得分:9)

非常感谢鲍里斯的帮助。以下是我通过评论中的互动发现的内容摘要:

1)核心问题是HTTP Request headers are supposed to be ISO-8859-1。 Chrome和Firefox的早期版本都在setRequestHeader()调用中未更改UTF-8字符串。这在FF24.0中发生了变化(很快就会在Chrome中发生变化),这样FF就会丢弃高字节,并且只传递每个字符的低字节。在我给出的问题的例子中,这是可以恢复的,但具有更高代码的字符可能会被无法挽回地损坏。

2)一种解决方法是在客户端进行编码,例如:

setRequestHeader('X-File-Name',encodeURIComponent(filename))

然后在服务器端解码,例如用PHP:

$filename=rawurldecode($_SERVER['HTTP_X_FILE_NAME'])

3)请注意,这只是有问题的,因为我的ajax文件上传方法是在请求体中发送原始文件数据,因此我需要通过自定义请求标头发送文件名(如许多教程在线中所示)。如果我改用FormData,我就不用担心了。我相信如果你想要基于标准的可靠的unicode文件名支持,你应该使用FormData而不是请求头方法。