IE浏览器下载中文名文件乱码,Header中Content-Disposition出现中文字导致的乱码问题

问题出现:

在项目中需要对上传到服务端的中文名文件进行下载,出现了下载文件名乱码的问题,而且这个问题只出现在IE浏览器上,如何解决这个问题,和大家分享解决方法,探讨其中的缘由。

参考文章:https://blog.robotshell.org/2012/deal-with-http-header-encoding-for-file-download/#comments

问题截图:
在这里插入图片描述

IE浏览器下载中文名文件乱码,Header中Content-Disposition出现中文字导致的乱码问题_第1张图片

解决思路:

实现下载功能肯定是通过设置HTTP Header中的Content-Disposition: attachment,再设置Content-Type要下载的文件类型即可实现文件的下载;
我们也知道我们可以通过在Header 中Content-Type 指定内容(body)的编码,如同下面这样:

header('Content-type:application/octet-stream; charset=utf-8');

我们在设置Content-Disposition的同时一般还会同时定义下载文件的文件名,如图我的文件名为$file_name这个变量:

header('Content-Disposition:attachment;filename="'.$file_name.'"');

但是如果我们的文件名出现了中文字符,而我们仅仅是定义了下载文件的内容(body)的编码我们该怎么定义header本身的编码?或者,Header是否允许非ASCII编码呢?

在参考的大佬文章中也有提到:

首先,根据 RFC 2616 所定义的 HTTP 1.1 协议( RFC 2068 是最早的版本;2616替代了2068并被最广泛使用,而后又被其他 RFC 替代,后文将会提及), HTTP 消息格式其实是基于古老的 ARPA Internet Text Messages ,而 ARPA 消息只能是 ASCII 编码的( RFC 822 Section 3 )。 RFC 2616 Section 2.2 更是再一次强调, TEXT( Section 4.2:Header 中的字段值即为 TEXT )中若要使用其他字符集,必须使用 RFC 2047 的规则将字符串编码/逃逸——必须要注意的是,这个规则原本是针对 MIME (电子邮件)的扩展,格式与百分号编码有很大不同。给一个在 MIME 中的例子:
Subject: =?ISO-8859-1?B?SWYgeW91IGNhbiByZWFkIHRoaXMgeW8=?=
在1999年 RFC 2616 推出之时, Content-Dispostion 这个 Header 尚不是正式 HTTP 协议的一部分,只不过是因为被广泛使用而从 MIME 标准中直接借用过来了而已( RFC 2616 Section 19.5.1 )。因而几乎没有浏览器去支持 Content-Disposition 的多语言编码特性这样一个“扩展特性的扩展特性”。事实上,RFC 2616 中建议的使用 RFC 2047 来进行多语言编码的特性从未被主流浏览器支持过,所以我们也不用操心上面这个 MIME 方案了……

总结而言,对于Content-Dispostion这个HTTP协议的后来成员,其多语言编码特性不被支持,各大浏览器采取解决措施由于缺乏统一的协议规定,各成一派,互不兼容!

  • IE支持在 filename 中直接使用百分号编码:filename="$encoded_text"(并非 MIME 编码!)。本来按照 RFC 2616 ,引号内的部分如果不是 MIME 编码,则应当直接被当作内容,就算它“看起来像是百分号编码后的字符串”;可是IE却会“自动”对这样的文件名进行解码——前提是该文件名必须有一个不会被编码的(即 ASCII)后缀名!
  • 其他一些浏览器则支持一种更为粗暴的方式:允许在 filename=“TEXT” 中直接使用 UTF-8 编码的字符串!这也是直接违反了 RFC 2616 HTTP 头必须是 ASCII 编码的规定。

显然第二种暴力方式对于我们来说更加人性化,直接使用UTF-8 编码,所以header中我们可以直接传入未被处理的中文文件名也不乱码,但这是违背协议的,但还是有很多浏览器“以身试法”(Chrome,QQ浏览器等)。
但是IE浏览器有自己的想法,支持在filename 中直接使用百分号编码,所以可以通过将文件名提前转化为百分号编码的方式进行处理。

终于统一标准下来了,
2010年 RFC 5987 发布,正式规定了 HTTP Header 中多语言编码的处理方式采用 parameter*=charset'lang'value 的格式。

  • charset 和 lang 不区分大小写。
  • lang 是用来标注字段的语言,以供读屏软件朗诵或根据语言特性进行特殊渲染,可以留空。
  • value 根据 RFC 3986 Section 2.1 使用百分号编码,并且规定浏览器至少应该支持 ASCII 和 UTF-8 。
  • 当 parameter 和 parameter* 同时出现在 HTTP 头中时,浏览器应当使用后者。

其好处是保持了向前兼容性:一来 HTTP 头仍然是 ASCII-only ,二来不支持该标准的旧版浏览器会按照当年 RFC 2616 的规定,把 parameter* 整体当作一个 field name ,从而当作一个未知的字段来忽略。随后,2011年 RFC 6266 发布,正式将 Content-Disposition 纳入 HTTP 标准。

最后

说了这么多,标准的处理方法是:

$file_name=rawurlencode($file_name);//将文件名转化为百分号编码
Content-Disposition: attachment;
                     filename="$file_name";
                     filename*=utf-8' '$file_name

其中rawurlencode()函数: http://www.php.net/rawurlencode
rawurlencode()函数对文件名进行预先的处理转化为协议中标准的百分号编码,被浏览器识别,
而被新浏览器识别的filename*则更是设置了其原编码格式,方便浏览器还原中文文件名。

你可能感兴趣的:(PHP,HTTP协议)