转载: 正确处理浏览器在下载文件时HTTP头的编码问题(Content-Disposition)
最近在做一个下载工具时,发现CSDN上的资源下载时竟然没有被拦截到,经过分析,终于有了一个发现,解决了我之前做文件下载时的乱码问题,所以转载这篇释疑文章,希望有人可以看到,可以从中得到帮助,也用来备忘。
CSDN中的下载时Header里的内容是这样的:
键 值
Content-Disposition attachment; filename*=UTF-8''Reflector.rar
针对上面这种写法的解释,主要是指filename后面的写法,于是转载以下内容,大家请看:
************************************************************************
最近在做项目时遇到了一个 case :需要实现一个强制在浏览器中的下载功能(即强制让浏览器弹出下载对话框),并且文件名必须保持和用户之前上传时相同(可能包含非 ASCII 字符)。
前一个需求很容易实现:使用 HTTP Header 的 Content-Disposition: attachment 即可,还可以配合 Content-Type: application/octet-stream 来确保万无一失。而后一个需求就比较蛋疼了,牵扯到 Header 的编码问题(文件名是作为 filename 参数放在 Content-Disposition 里面的)。众所周知, HTTP Header 中的 Content-Type 可以指定内容的编码,可 Header 本身的编码又该如何制定?甚至, Header 究竟是否允许非 ASCII 编码呢?
如果放任编码问题不管,那么恭喜你,你一定会遇到在某个系统及浏览器下下载文件时文件名乱码的情况。如果你尝试搜索解决,那么再一次恭喜你,你会找到一堆自相矛盾的解决方案(我可以负责任地告诉你,其中的99%都是不符合标准的 trick 罢了)。让我们来看看到底应该如何优雅完美地解决这个问题吧!
为了探索这个问题,我走了不少弯路。从自己尝试,到 Google 、百度(分别尝试过中英文搜索),再到阅读 Discuz 等经典项目的源码,众说纷纭、莫衷一是。最后我才想到回归 RFC ,从标准文档中找办法,果然有所收获。由于探究过程实在太曲折,我就先把标准做法写下来。
应该这样设置 Content-Disposition :
Content-Disposition: attachment;
filename="$encoded_fname";
filename*=utf-8''$encoded_fname其中,$encoded_fname指的是将 UTF-8 编码的原始文件名按照 RFC 3986 进行百分号 urlencode 后得到的( PHP 中使用 rawurlencode() 函数)。这几行也可以合并为一行,推荐使用一个空格隔开。
另外,为了兼容 IE6 ,请保证原始文件名必须包含英文扩展名!
好了,接下来我们来看看为什么要这么做以及为什么能这么做。
首先,根据 HTTP 1.1 协议规范( RFC 2616 Section 4 ), HTTP 消息格式其实是基于古老的 ARPA INTERNET TEXT MESSAGES ( RFC 822 Section 3 ),根据其规定,消息只能是 ASCII 编码的。 RFC 2616 Section 2.2 又一次强调, TEXT 中若要使用其他字符集,必须使用 RFC 2047 的规则将字符串编码为 ASCII 码(事实上这个规则原本是针对 MIME 的扩展,使用的是 base64 编码,格式与百分号编码有很大不同)。总而言之,按照标准, HTTP Header 中的文本数据必须是 ASCII 编码的。
filename="TEXT"
;这是 RFC 2616 标准,TEXT必须是 ASCII 字符且被认为就是“原文”
filename*=charset'lang'encoded-text
;这是按照 RFC 2047 扩展后的,注意格式上的细微区别,采用 base64 编码(编码结果也是 ASCII 字符)
然而,事实上在1999年 HTTP 1.1 标准推出之时, Content-Dispostion 这个 Header 尚不是正式标准的一部分,只不过是因为被广泛使用而从 MIME 标准中直接借用过来了而已( RFC 2616 Section 19.5.1 )。因而几乎没有浏览器去支持 Content-Disposition 的多语言编码特性这样一个“扩展特性的扩展特性”(事实上, HTTP 1.1 草案中建议的使用 RFC 2047 来进行多语言编码的特性从未被主流浏览器支持过)。
可是这个问题却的确是现实需要的,所以浏览器就各自想出了一些办法:
- IE支持两种格式的混合版:filename="encoded_text" (这里采用的是百分号编码)。本来按照 RFC 2616 ,引号内的部分应当直接被当作内容,就算它“看起来像是编码后的字符串”;可是IE却会“自动”对这样的文件名进行解码——前提是该文件名必须有一个不会被编码的后缀名(即正常的英文字母后缀名)!
- 其他一些浏览器则支持一种更为粗暴的方式——允许在 filename="TEXT" 中直接使用 UTF-8 编码的字符串!
这两类浏览器的行为是彼此互不兼容的。所以你可以判断 UA 然后对IE使用前一种办法,其他浏览器使用后一种,这样便可以达到一般情况下能够 just work 的效果( Discuz 就是这么做的)。不过对于 Opera 和 Safari ,这样做可能不一定有效。
时代在进步,2010年 RFC 5987 发布,正式规定了 HTTP Header 中多语言编码的处理方式,应当采用类似 MIME 扩展的 parameter*=charset'lang'value 的格式,但是其中 value 应根据 RFC 3986 Section 2.1 使用百分号进行编码,并且规定浏览器至少应该支持 ASCII 和 UTF-8 。随后,2011年 RFC 6266 发布,正式将 Content-Disposition 纳入 HTTP 标准,并再次强调了 RFC 5987 中多语言编码的方法,还给出了一个范例用于解决向后兼容的问题——就是我在一开始给出的例子:
Content-Disposition: attachment;
filename="encoded_text";
filename*=utf-8''encoded_text
在这个例子中,对于较新的 Firefox 、 Chrome 、 Opera 、 Safari 等浏览器,都支持新标准规定的 filename* ,并且会优先使用,所以尽管 filename=”encoded_text” 不被它们支持,仍然不会有问题;至于使用 UTF-8 只是因为它是标准中强制要求必须支持的。而对于旧版本的IE浏览器,它们无法识别后面的 filename* ,会自动忽略并使用旧的 filename 。这样一来就完美解决了多浏览器的多语言兼容问题,既不需要 UA 判断,也符合标准。
P.S. 为什么 PHP 要使用 rawurlencode() 函数呢?因为这才是真正符合 RFC 3986 的“百分号URL编码”,只是由于历史原因,之前先有了一个 urlencode() 函数用于实现 HTTP POST 中的类似的编码规则,故而只好用这么一个奇怪的名字。两者的区别在于前者会把空格编码为%20,而后者则会编码为+号。如果使用后者,那么IE6在下载带有空格的文件名时空格会变为加号。一般情况下,你是不会用到 urlencode() 这个函数的( Discuz 某些版本中错误地使用它来进行文件名编码,从而导致空格变加号的BUG)。
转载: 正确处理浏览器在下载文件时HTTP头的编码问题(Content-Disposition)的更多相关文章
- 正确处理下载文件时HTTP头的编码问题(Content-Disposition)
留坑 参考: 正确处理下载文件时HTTP头的编码问题(Content-Disposition) HTTP协议header中Content-Disposition中文文件名乱码 文件下载,content ...
- Firefox下载文件时中文名乱码问题
为了形象化,先看几张不同浏览器下下载文件时的效果图: 1:Firefox 36.0.1 2:IE8 3:Chrome 40.0.2214.93 m 4:360 7.1.1.322 很明显在Firefo ...
- 使用HttpURLConnection下载文件时出现 java.io.FileNotFoundException彻底解决办法
使用HttpURLConnection下载文件时经常会出现 java.io.FileNotFoundException文件找不到异常,下面介绍下解决办法 首先设置tomcat对get数据的编码:con ...
- C#中解决Response.AddHeader("Content-Disposition", "attachment; filename=" + filename)下载文件时文件名乱码的问题
问题:下载文件时文件名乱码怎么解决? 在C#写后台代码过程中,经常遇到下载文件出现文件名乱码的问题,在网上找了很多方法,总是存在浏览器不兼容的问题,当IE浏览器不乱码时,火狐浏览器就会乱码,后来经过反 ...
- 下载文件时-修改文件名字 Redis在Windows中安装方法 SVN安装和使用(简单版) WinForm-SQL查询避免UI卡死 Asp.Net MVC Https设置
下载文件时-修改文件名字 1后台代码 /// <summary> /// 文件下载2 /// </summary> /// <param name="Fil ...
- java下载文件时文件名出现乱码的解决办法
转: java下载文件时文件名出现乱码的解决办法 2018年01月12日 15:43:32 橙子橙 阅读数:6249 java下载文件时文件名出现乱码的解决办法: String userAgent ...
- 由于想要实现下载的文件可以进行选择,而不是通过<a>标签写死下载文件的参数,所以一直想要使用JFinal结合ajax实现文件下载,但是ajax实现的文件下载并不能触发浏览器的下载文件弹出框,这里通过模拟表单提交实现同样的效果。
由于想要实现下载的文件可以进行选择,而不是通过<a>标签写死下载文件的参数,所以一直想要使用JFinal结合ajax实现文件下载(这样的话ajax可以传递不同的参数),但是ajax实现的文 ...
- .net 直接输出远程文件到浏览器和下载文件保存到本机
利用了xmlhttp,实现代码比较简单具体实现如下: 首先bin文件引入,com->microsoft xml v3.0 具体代码如下: protected void Button1_Click ...
- 【转】【Python】python使用urlopen/urlretrieve下载文件时出现403 forbidden的解决方法
第一:urlopen出现403 #!/usr/bin/env python # -*- coding: utf- -*- import urllib url = "http://www.go ...
随机推荐
- 将回车键转tab键
//功能:将回车键转tab键$(function () {$('input:text:first').focus();var $enter = $("input[type=text],but ...
- 原!!jar包 --可执行exe文件--安装包
这几天由于部门统计名单,都是一边报,一边统计,感觉麻烦,写了个小工具,做成安装包.其他不多说,网上都有,我就自己按照网上操作,碰到了一些问题,对这些问题说下. ----------废话少说------ ...
- Centos7 搭建 Keepalived+LVS 备注
NAT模型需要RealServer gateway设定为,DR模式需要执行 RealServer.sh.需要先安装network-tools. #!/bin/bash#description : st ...
- cookie预:
什么是cookie? cookie 是存储于访问者的计算机中的变量.每当同一台计算机通过浏览器请求某个页面时,就会发送这个 cookie.你可以使用 JavaScript 来创建和取回 cookie ...
- H5页面左图右边文字如何布局
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8&qu ...
- myeclipse注册码生成器
import java.io.BufferedReader;import java.io.IOException;import java.io.InputStreamReader; public cl ...
- hdu 1032
题目的意思是把输入的i,j 从i到j的每一个数 做循环,输出循环次数最大的值 易错的地方:做循环是容易直接用i进行计算 i=i/2:或i=i*3+1: 这样i的值改变就不能在做下面数的循环 #incl ...
- PDF 补丁丁 0.4.2.905 测试版发布:智能合并功能减小合并文件的大小
之前的测试版在合并文件功能处添加两个相同的PDF文件,程序会重复写入该文件的内容,导致文件体积膨胀,浪费存储空间. 例如:使用者需要在原 PDF 文件(设文件为1.pdf)第2和第3页中间插入几张图片 ...
- HDU 1372 Knight Moves
最近在学习广搜 这道题同样是一道简单广搜题=0= 题意:(百度复制粘贴0.0) 题意:给出骑士的骑士位置和目标位置,计算骑士要走多少步 思路:首先要做这道题必须要理解国际象棋中骑士的走法,国际象棋中 ...
- android单选框和复选框(练习)
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android=&quo ...