我的问题是如何在 HTTP 中对 Content-Disposition 标头的文件名参数进行编码? 但是由于这个问题是很久以前提出的并且仍然没有令人满意的答案(在我看来),我想再问一次。
我开发了一个 C++ CGI 应用程序,它提供的文件名称中可能包含特殊字符,例如
“奇怪的 # € = { } ; filename.txt ”
似乎不可能以适用于每个浏览器的方式设置 HTTP Content-Dispostion,例如
- IE浏览器
- 火狐
- 铬合金
- 歌剧
- 苹果浏览器
我很乐意为每个浏览器提供不同的解决方案。
现在这就是我走了多远:
Internet Explorer(添加了双引号并替换了 # 和 ; )
Content-Disposition: attachment; filename="weird %23 € = { } %3B filename.txt"
Firefox(双引号似乎有效。没什么可做的):
Content-Disposition: attachment; filename="weird # € = { } ; filename.txt"
另一个可行的选择:
Content-Disposition: attachment; filename*=UTF-8''weird%20%23%20%e2%82%ac%20%3D%20%7B%20%7D%20%3B%20filename.txt
铬合金
当仅使用双引号时,会出现这些问题:
- = 在文件名中消失
- € 将被替换为 -
但这有效:
Content-Disposition: attachment; filename*=UTF-8''weird%20%23%20%e2%82%ac%20%3D%20%7B%20%7D%20%3B%20filename.txt
歌剧
使用双引号或使用语法:filename*=UTF-8''... 会产生以下问题:
- 文件名中的多个粘贴在一起的空格减少到一个
- { 和 } 消失:“ ab{}cd.txt ” -> “ abcd.txt ”
- 文件名在 ; 之后被截断 其中:“ abc ; def.txt ”->“ abc ”
编辑 2:这是因为文件名长度限制。此语法适用于 Opera:
Content-Disposition: attachment; filename*=UTF-8''weird%20%23%20%e2%82%ac%20%3D%20%7B%20%7D%20%3B%20filename.txt
苹果浏览器
€ 将替换为不可见字符(使用双引号)
no solution that prevents that little problem
来自另一个线程(上面提到的)的建议使用
Content-Disposition: attachment; filename*=UTF-8''weird%20%23%20%80%20%3D%20%7B%20%7D%20%3B%20filename.txt
对我不起作用。转义字符不会被翻译回来,或者浏览器想要以我的 cgi 应用程序的名称保存到文件中。那是因为我的编码错误。我没有根据 RFC 5987 进行编码。但是 Safari 无论如何都没有使用这种编码。所以到目前为止还没有解决 € 字符的方法。
顺便说一句:UTF-8 转换器http://www.rishida.net/tools/conversion/
我在这些测试中使用了每个浏览器的最新版本:
- 火狐 7
- 互联网浏览器 9
- 铬 15
- 歌剧 11.5
- 野生动物园 5.1
PS:我在键盘上尝试了所有特殊字符。我在这个线程中只使用了那些制造麻烦的。
编辑:
我还在我的键盘上尝试了一个包含所有特殊字符的文件名(可能在文件名中),但它不像上面的测试字符串那样工作:
完整的测试字符串:
0 ! § $ % & ( ) = ` ´ { } [ ] ² ³ @ € µ ^ ° ~ + ' # - _ . , ; ü ä ö ß 9.jpg
编码测试字符串:
0%20%21%20%C2%A7%20%24%20%25%20%26%20%28%20%29%20%3D%20%60%20%C2%B4%20%7B%20%7D%20%20%20%20%5B%20%5D%20%C2%B2%20%C2%B3%20%40%20%E2%82%AC%20%C2%B5%20%5E%20%C2%B0%20~%20%2B%20%27%20%23%20-%20_%20.%20%2C%20%3B%20%C3%BC%20%C3%A4%20%C3%B6%20%C3%9F%209.jpg
使用这种方法:
Content-Disposition: attachment; filename*=UTF-8''0%20%21%20%C2%A7%20%24%20%25%20%26%20%28%20%29%20%3D%20%60%20%C2%B4%20%7B%20%7D%20%20%20%20%5B%20%5D%20%C2%B2%20%C2%B3%20%40%20%E2%82%AC%20%C2%B5%20%5E%20%C2%B0%20~%20%2B%20%27%20%23%20-%20_%20.%20%2C%20%3B%20%C3%BC%20%C3%A4%20%C3%B6%20%C3%9F%209.jpg
我有以下结果:
- 火狐的作品
- 铬作品
- 即: $ % & ( ) = ` ´ { } [ ] ² ³ @ € µ ^ ° ~ + ' # - _ 。, ; ü ä ö ß 9.jpg(删除前 6 个字符)。编辑 2:这是因为浏览器的文件名长度限制。它开始从字符串的开头切断文件名。我没有深入研究,但看起来普通文件名可能有大约 200 个字符长,而具有许多转义序列的文件名甚至更多但少于 250 个。但这没关系。
- 歌剧:0!§ $ % & ( ) = ` ´ [ ] ² ³ @ € µ ^ ° ~ + ' # - _ 。, ; ü ä ö ß 9.jpg(像以前一样缺少一些字符)。编辑 2:我缩短了我的测试字符串,因为我怀疑 Opera 的文件名长度“问题”,就像 IE 一样,它也在那里工作。
- Safari 不适用于该语法。那是例外。
编辑2:
到目前为止的状态是,语法filename*=UTF-8''filname 转义序列“适用于除 Safari 之外的所有浏览器。唯一被 Safari 替换的字符是€。我想我可以忍受。谢谢你!
编辑 3:文件名长度
我注意到一些文件名长度问题。
- Internet Explorer:文件名可以是 147 个字符。如果字符串不包含转义序列,那么这就是文件名的长度。如果是这样,文件名可能会有所不同。生成的文件名少于 147 个字符。但它不同。我使用了 2 个转义序列,文件名缩短了 5 个字符,我使用了许多转义序列,文件名缩短了 2 个字符。我在这里找不到规则。
- 其他浏览器似乎没有这个问题。如果文件系统可以处理文件,他们会保存文件。例如,我尝试了 250 个字符,浏览器说我必须减少文件名 (Chrome),或者他们自己将其缩短为 220 (Opera) 或 210 (Firefox) 字符。Opera 切断了文件的结尾。Safari 尝试保存那个长文件名,但最终没有保存它,而是在下载列表中写入“-1”作为文件名。