转:HTTP ---HTTP头的编码问题(Content-Disposition)
最近在做项目时遇到了一个 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)。 via:Robot Shell
Content-disposition 是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。当 Internet Explorer 接收到头时,它会激活文件下载对话框,它的文件名框自动填充了头中指定的文件名。(请注意,这是设计导致的;无法使用此功能将文档保存到用户的计算机上,而不向用户询问保存位置。)
Content-Disposition就是当用户想把请求所得的内容存为一个文件的时候提供一个默认的文件名。具体的定义如下
Content-Disposition: attachment; filename=“filename.xls”
当然filename参数可以包含路径信息,但User-Agnet会忽略掉这些信息,只会把路径信息的最后一部分做为文件名。当你在响应类型为application/octet- stream情况下使用了这个头信息的话,那就意味着你不想直接显示内容,而是弹出一个”文件下载”的对话框,接下来就是由你来决定“打开”还是“保存”了。
如:Response.AppendHeader("Content-Disposition","attachment;filename=MyExcel.xls");
Content disposition 允许 servlet 指定文档表示的信息
HTTP response header中的content-disposition 允许 servlet 指定文档表示的信息。使用这种header ,你就可以将文档指定成单独打开(而不是在浏览器中打开),还可以根据用户的操作来显示。如果用户要保存文档,你还可以为该文档建议一个文件名。这个建议 名称会出现在 Save As 对话框的“文件名”栏中。如果没有指定,则对话框中就会出现 servlet 的名字。
servlet 中,将 header 设置成下面这样:
response.setHeader("Content-disposition","attachment;filename="+ "Example.xls" );
response.setHeader("Content-Disposition", "inline; filename="fliename);//点击打开会在ie中打开。
需要说明的有三点:
(1) 中文文件名需要进行iso8859-1转码方可正确显示:
fileName = new String(fileName.getBytes("GBK"),"iso8859-1");
(2)传递的文件名,需要包含后缀名(如果此文件有后缀名),否则丢失文件的属性,而不能自行选择相关程序打开。
(3)有下载前询问(是打开文件还是保存到计算机)和通过IE浏览器直接选择相关应用程序插件打开两种方式,前者如上代码所示,后者如下:
response.setHeader("Content-disposition","filename="+ "Example.xls" );
转:HTTP ---HTTP头的编码问题(Content-Disposition)的更多相关文章
- 正确处理下载文件时HTTP头的编码问题(Content-Disposition)
留坑 参考: 正确处理下载文件时HTTP头的编码问题(Content-Disposition) HTTP协议header中Content-Disposition中文文件名乱码 文件下载,content ...
- javaWeb项目-文件下载的消息头和编码问题
一.问题: 做web项目经常提到的一个需求就是页面的文件下载,那么下载的时候在后台为什么要设置响应消息头?为什么这样设置? 二.解决: 1.例子 //设置响应的消息头response.setConte ...
- 签名、BOM头、编码、Windows记事本编码、java编码解码的那些事
对于Windows记事本: ANSI :GB2312 java中应使用GBK解码 Unicode :有签名的UTF-16LE java中应使用UTF-16解码 Unicode big endian : ...
- html特殊字符 编码css3 content:"我是特殊符号"
项目中用到的一些特殊字符和图标 html代码 <div class="cross"></div> css代码 .cross{ width: 20px; he ...
- 转载: 正确处理浏览器在下载文件时HTTP头的编码问题(Content-Disposition)
最近在做一个下载工具时,发现CSDN上的资源下载时竟然没有被拦截到,经过分析,终于有了一个发现,解决了我之前做文件下载时的乱码问题,所以转载这篇释疑文章,希望有人可以看到,可以从中得到帮助,也用来备忘 ...
- 转载:html特殊字符 编码css3 content:&quot;我是特殊符号&quot;
项目中用到的一些特殊字符和图标 html代码 <div class="cross"></div> css代码 .cross{ width: 20px; he ...
- html特殊字符 编码css3 content:"特殊符号"一览
工作中经常会用到用纯css3美化复选框 <div class="cross"></div> css代码.cross{ width: 20px; height ...
- 爬虫技术 -- 基础学习(五)解决页面编码识别(附c#代码)
实现从Web网页提取文本之前,首先要识别网页的编码,有时候还需要进一步识别网页所使用的语言.因为同一种编码可能对应多种语言,例如UTF-8编码可能对应英文或中文等语言. 识别编码整体流程如下: (1) ...
- 超全面的JavaWeb笔记day10<Response&Request&路径&编码>
1.Response 2.Request 3.路径 4.编码 请求响应流程图 response 1.response概述 response是Servlet.service方法的一个参数,类型为java ...
随机推荐
- Android 横向列表GridView 实现横向滚动
Android 横向列表实现,可左右滑动,如下图 1.主界面布局代码:activity_main.xml a.包裹HorizontalScrollView控件是GirdView横向滚动的基本条件b.G ...
- 通栏导航栏的制作,综合使用CSS属性,代码不超过30行
这篇文章,小编带领大家一同做一个利用CSS技术实现的导航栏.通过这个导航栏的制作,希望大家能够对前几篇文章中学习到的CSS属性能有一个整体的认识,并能够达到灵活运用的程度. 承接文章:灵活控制块级元素 ...
- CSS浮动属性,知道原理就很简单,灵活控制块级元素在一行内显示
在页面布局中,有两个非常常用的CSS属性.它们巧妙的控制着块级元素们之间的位置,灵活的让块级元素在一行内显示或者另起一行.说到这里,相信大家已经猜出来了,这两个属性就是控制块级元素浮动的属性.整个页面 ...
- HDU 2602.Bone Collector-动态规划0-1背包
Bone Collector Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)To ...
- Properties文件工具读取类
import java.io.IOException;import java.io.InputStream;import java.util.Properties; public class Comm ...
- 代码编辑器[0] -> Vim/gVim[0] -> 基于 Python 的 gVim 环境配置(Windows)
环境配置 / Environment Setup 基于Python开发的 gVim 环境配置(Windows) 使用方式参考 Vim 的使用. 1 基于vundle进行配置 Vim有多个扩展管理器, ...
- Codeforces 1025D Recovering BST
这个题被wa成傻逼了.... ma[i][j]表示i,j能不能形成一条直接作为排序二叉树的边,n^3更新维护ma即可,按说应该是要爆复杂度的,数据玄学吧.. #include<iostream& ...
- ELK帮助文档
elasticsearch: API中文指南:https://es.xiaoleilu.com/010_Intro/15_API.html 官方文档:https://www.elastic.co/cn ...
- redis --- lua 脚本实现原子操作
如题, 楼主的想法很简单, lua 脚本本身支持原子性, 所以把命令写进一个脚本就行, 当然后续还会优化才能放到生产上,例如缓存脚本 ,redis 本身会缓存执行过的脚本 ,这样速度更快, 再优化, ...
- [置顶]
kubernetes资源类型--deployment
Deployment(中文意思为部署.调度)提供了一种更加简单的更新RC和Pod的机制,K8S版本1.2实现的.通过在Deployment中描述所期望的集群状态,Deployment Controll ...