Content-Disposition中filename字段的字符编码技巧[转]

本文是关于“如何编码http包的Content-Disposition中的filename字段?”这个问题的又一次探讨。这个问题在很久之前被提出来以后,到现在仍然没有满意的答案,至少我认为是这样的,所以今天我再次把这个问题抛出来,附上我的解决办法。

我编写了一个基于C++的CGI应用,他可以解析包含特殊字符文件名的文件,比如像这样:weird # € = { } ; filename.txt。

似乎没有一种通用方法来搞定HTTP中的Content-Dispostion使之可以在每个浏览器中都正常工作,测试的浏览器有以下:

  • Internet Explorer
  • Firefox
  • Chrome
  • Opera
  • Safari

不过我倒是很乐意针对不同的浏览器用不同的办法来编码。

下面就说说我采用的办法:

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

Chrome:

只用双引号的时候会出现下面这些问题:

  1. 文件名中的=符号会丢失
  2. €会被替换成-符号

用这种办法就能搞定了:

Content-Disposition: attachment; filename*=UTF-8‘‘weird%20%23%20%e2%82%ac%20%3D%20%7B%20%7D%20%3B%20filename.txt

Opera:

用双引号或者语法:filename*=UTF-8”,会产生下面这些问题:

  1. 文件名中的多个连续空格只剩下一个
  2. 成对的{}丢失:”ab{}cd.txt” -> “abcd.txt”
  3. 文件名中的分号会截断后面的字符:”abc ; def.txt” -> “abc”

这是由于文件名长度限制造成的,下面的例子可以在Opera中工作:

Content-Disposition: attachment; filename*=UTF-8‘‘weird%20%23%20%e2%82%ac%20%3D%20%7B%20%7D%20%3B%20filename.txt

Safari:

如果用双引号,€符号会被替换成不可见字符,很遗憾没有合适的办法来解决这个小问题。

但是从上面提到的那个问题原文中得到一个可以参考的办法:

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/

上文提到的所有测试均用了当前最新版本的浏览器:

  • Firefox 7
  • Internet Explorer 9
  • Chrome 15
  • Opera 11.5
  • Safari 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用这种方式来写:

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

得到了如下测试结果:

火狐可以正常工作

Chrome可以正常工作

IE显示:$ % & ( ) = ` ´ { } [ ] ² ³ @ € µ ^ ° ~ + ‘ # – _ . , ; ü ä ö ß 9.jpg 丢失了头6个字符

解释下:出现这个问题是因为浏览器对文件名字符长度的限制造成的:从字符串的开头舍弃一些字符。我没有深入挖掘这一点,不过正常的文件名大约可以长达大概200个字符,那些文件名包含许多转义字符序列的长度甚至可以更多,但是不超过250个字符。所以说这个其实没啥问题。

Opera:0 ! § $ % & ( ) = ` ´ [ ] ² ³ @ € µ ^ ° ~ + ‘ # – _ . , ; ü ä ö ß 9.jpg 和IE中的那样也丢失了一些字符。

说明:我缩减过我的测试字符串,因为我怀疑Opera中也有像IE那样的长度限制问题。

Safari中这种编码无法正常使用。

测试到现在说明一个事实:形如“filename*=UTF-8”filname escape sequence”这样的语法可在除了safari外的其他浏览器中正常工作。但是在Safari中用这种方法也只有€会被替换掉。所以这个问题不大。

关于文件名长度:

测试中发现了一些文件名长度的问题。

在Internet Explorer中:文件名长度最多可以长达147个字符。如果字符串中没有出现转移字符的话,这就是文件名的总长度了。如果字符串中出现了转移字符,情况就有些变化。最终的文件名长度小于147个字符。但是规则有点奇怪,我找不出一个准确的规则。如果我用了两个转义字符,文件名缩短了5个字符长度,但是我用很多转义字符的话文件名最终只缩短了两个字符。

其他浏览器没有这个长度限制问题。只要系统能够处理这个文件,它就能顺利保存这个文件。我测试了下250个字符长度的文件名,chrome提示我要缩减文件名长度,opera会自动缩减到220个字符,火狐自动缩减为210个字符。Opera会从文件名末尾开始缩减。Safari尝试保存那么长的文件名,但是处理失败无法保存,同时在下载列表的文件名中会显示成-1。

时间: 2024-10-14 21:09:31

Content-Disposition中filename字段的字符编码技巧[转]的相关文章

Python中的字符串与字符编码

原文地址:点击这里 本节内容: 前言 相关概念 Python中的默认编码 Python2与Python3中对字符串的支持 字符编码转换 一.前言 Python中的字符编码是个老生常谈的话题,同行们都写过很多这方面的文章.有的人云亦云,也有的写得很深入.近日看到某知名培训机构的教学视频中再次谈及此问题,讲解的还是不尽人意,所以才想写这篇文字.一方面,梳理一下相关知识,另一方面,希望给其他人些许帮助. Python2的 默认编码 是ASCII,不能识别中文字符,需要显式指定字符编码:Python3的

JavaScript中进制和字符编码问题

1.进制: JavaScript中允许使用字面量的形式声明不同进制的数字: var a = 0b10; // 2 声明一个二进制 var b = 010; // 8 八进制,严格模式下会报错 var c = 10; // 10 十进制 var d = 0x10 // 16 十六进制 其中八进制字面量在严格模式下会报错,数字超过范围,将会忽略前导 0 ,解析为十进制数字 var f = 08 ; //8 2.进制之间转换: 调用toString方法,可以在进制之间进行转换. d.toString(

浅析白盒审计中的字符编码及SQL注入

尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范.但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的一套编码,比如gbk,作为自己默认的编码类型.也有一些cms为了考虑老用户,所以出了gbk和utf-8两个版本. 我们就以gbk字符编码为示范,拉开帷幕.gbk是一种多字符编码,具体定义自行百度.但有一个地方尤其要注意: 通常来说,一个gbk编码汉字,占用2个字节.一个utf-8编码的汉字,占用3个字节.在php中

.NET Framework 中的字符编码

字符是可用多种不同方式表示的抽象实体. 字符编码是一种为受支持字符集中的每个字符进行配对的系统,配对时使用的是表示该字符的某些值. 例如,摩尔斯电码是一种为罗马字母表中的每个字符进行配对的字符编码,配对时使用的是适合在电报线路中传输的点和线模式. 计算机的字符编码将所支持字符集中的每个字符与代表该字符的数值进行配对.字符编码具有两个不同的组件: 编码器,将字符序列转换为数值序列(字节). 解码器,将字节序列转换为字符序列. 字符编码描述了编码器和解码器的操作规则. 例如,UTF8Encoding

JAVA中文字符编码问题

JAVA的中文字符乱码问题一直很让人头疼.特别是在WEB应用中.网上的分析文章和解决方案都很多,但总是针对某些特定情况的.很多次遇到乱码问 题后, 经过极为辛苦的调试和搜索资料后终于解决,满以为自己已经掌握了对付这些字符乱码怪兽的诀窍.可当过段时间,换了个应用或换了个环境,又会碰到那讨厌的火 星文,并再次无所适从.于是下决心好好整理一下中文字符编码问题,以方便自己记忆,也为其他程序员兄弟们提供一份参考. 首先要了解JAVA处理字符的原理.JAVA使用UNICODE来存储字符数据,处理字符时通常有

[转] Unicode字符编码区间表

firebug 打UTF8 字符: var res = ""; for(var i=0x2000;i< 0x2bff ;i++){ res += i.toString(16) + "\t:" + unescape ( "%u" + i.toString(16) ) +"\t\t" ; } res http://www.j2megame.org/index.php/content/view/2387/125.html Un

字符编码【转载】

转载:http://www.techug.com/character-encoding 字符集和字符编码 编程技术2015-3-26TECHUG2条评论 本文将简述字符集,字符编码的概念.以及在遭遇乱码时的一些常用诊断技巧. 背景:字符集和编码无疑是IT菜鸟甚至是各种大神的头痛问题.当遇到纷繁复杂的字符集,各种火星文和乱码时,问题的定位往往变得非常困难.本文就将会从原理方面对字符集和编码做个简单的科普介绍,同时也会介绍一些通用的乱码故障定位的方法以方便读者以后能够更从容的定位相关问题.在正式介绍

十分钟搞清字符集和字符编码

本文将简述字符集,字符编码的概念.以及在遭遇乱码时的一些常用诊断技巧. 背景:字符集和编码无疑是IT菜鸟甚至是各种大神的头痛问题.当遇到纷繁复杂的字符集,各种火星文和乱码时,问题的定位往往变得非常困难.本文就将会从原理方面对字符集和编码做个简单的科普介绍,同时也会介绍一些通用的乱码故障定位的方法以方便读者以后能够更从容的定位相关问题.在正式介绍之前,先做个小申明:如果你希望非常精确的理解各个名词的解释,那么可以查阅wikipedia.本文是博主通过自己理解消化后并转化成易懂浅显的表述后的介绍.

操作系统和程序设计语言的API使用的字符编码分析

 1.Java的运行环境中,String是什么编码? 使用java做程序设计语言,字符编码是和jvm相关的,和操作系统无关. java默认的编码是jvm在安装的时候就确定了的,它是根据你的系统的环境确定并默认的. 我们可以通过java的Charset类的defaultCharset()方法来获取它默认的字符编码. 我安装的JDK是1.7,系统环境是64位,获取的默认的字符编码是utf16的,并且是Big-Endian(这点我比较奇怪,我的机器是Little-endian的,而虚拟机竟然默认是大端