哦,字符编码的乐趣!
这里发生的事情如下。Ruby 在内部将您提取的字符串存储为字节序列,即文件名的 utf-8 编码。当您调用URI.escape
它时,这些字节以%xy
格式进行转义,并且生成的字符串现在仅由 ASCII 范围内的字节组成,用作 url。
然而,接收服务器正在解释这些字节(在将它们从%xy
表单中转义之后),就好像它们采用不同的编码一样,在这种情况下是ISO-8859-1,因此它提供的结果文件名与它所拥有的任何内容都不匹配.
这是一个使用 Ruby 1.9 的演示,因为它对编码有更好的支持。
1.9.3-p194 :003 > f
=> "ÖÇÄÜ360ÓïÒôÖúÀí.txt"
1.9.3-p194 :004 > f.encoding
=> #<Encoding:UTF-8>
1.9.3-p194 :005 > URI.escape f
=> "%C3%96%C3%87%C3%84%C3%9C360%C3%93%C3%AF%C3%92%C3%B4%C3%96%C3%BA%C3%80%C3%AD.txt"
1.9.3-p194 :006 > g = f.encode 'iso-8859-1'
=> "\xD6\xC7\xC4\xDC360\xD3\xEF\xD2\xF4\xD6\xFA\xC0\xED.txt"
1.9.3-p194 :007 > g.encoding
=> #<Encoding:ISO-8859-1>
1.9.3-p194 :008 > URI.escape g
=> "%D6%C7%C4%DC360%D3%EF%D2%F4%D6%FA%C0%ED.txt"
因此,这种情况下的解决方案是在转义字符串之前将其编码为 ISO-8859-1。在 Ruby 1.9 中,您可以像上面那样执行此操作,在早期版本中,您可以使用 Iconv(我假设 JRuby 包含 Iconv,我实际上对 JRuby 并不熟悉):
1.8.7 :001 > f
=> "\303\226\303\207\303\204\303\234360\303\223\303\257\303\222\303\264\303\226\303\272\303\200\303\255.txt"
1.8.7 :005 > g = Iconv.conv('iso-8859-1', 'utf-8', f)
=> "\326\307\304\334360\323\357\322\364\326\372\300\355.txt"
1.8.7 :006 > URI.escape f
=> "%C3%96%C3%87%C3%84%C3%9C360%C3%93%C3%AF%C3%92%C3%B4%C3%96%C3%BA%C3%80%C3%AD.txt"
1.8.7 :007 > URI.escape g
=> "%D6%C7%C4%DC360%D3%EF%D2%F4%D6%FA%C0%ED.txt"
请注意,通常您不能依赖使用任何特定编码的服务器。它应该使用 utf-8,但在这种情况下显然不是。