瀏覽器下載中文名文件出現(xiàn)亂碼途凫,如何解決垢夹?

《web開(kāi)發(fā)中,如何讓瀏覽器下載文件?》 中介紹了如何讓瀏覽器下載文件的原理维费,其實(shí)非常簡(jiǎn)單果元,本質(zhì)上就是輸出一個(gè) HTTP header 頭(其實(shí)是一個(gè) MIME header 頭),但對(duì)于中國(guó)開(kāi)發(fā)者來(lái)說(shuō)犀盟,更實(shí)際的問(wèn)題是:在某些瀏覽器上而晒,如果下載的文件是中文名,那很有可能出現(xiàn)亂碼阅畴。

實(shí)際上導(dǎo)致這一亂現(xiàn)的并不是技術(shù)問(wèn)題倡怎,而是標(biāo)準(zhǔn)的問(wèn)題,Web 發(fā)展到如今恶阴,經(jīng)歷了太多的混亂诈胜,為了把亂碼這個(gè)問(wèn)題說(shuō)清楚,我花了一天時(shí)間冯事,尋找了一些相關(guān)資料焦匈。

先上圖吧,這是我在 google 上找到的:

20-browser-rfc.png

這個(gè)圖由三部分 RFC 組成昵仅,分別是 MIME缓熟、Content-Disposition HEADER累魔、HTTP。

首先 Content-Disposition 這個(gè)頭由 MIME 定義够滑,rfc2231(MIME Parameter Value and Encoded Word Extensions:Character Sets, Languages, and Continuations)是最新的標(biāo)準(zhǔn)垦写,描述了 MIME 中字符集及語(yǔ)言的標(biāo)準(zhǔn),廢棄了 rfc2184 和 rfc204彰触。

rfc2183 基于 rfc2231 定義了在互聯(lián)網(wǎng)上 Content-Disposition 頭的標(biāo)準(zhǔn)梯投,說(shuō)明了字符集及編碼,可以這樣理解 rfc2183 和 rfc2231 是從不同角度建立的標(biāo)準(zhǔn)况毅,雙方并不沖突分蓖。

現(xiàn)在來(lái)說(shuō) HTTP,因?yàn)閬y碼問(wèn)題是在瀏覽器中產(chǎn)生的尔许,也就是說(shuō)肯定和 HTTP 協(xié)議有關(guān)么鹤,rfc2616 是 HTTP/1.1 協(xié)議的總標(biāo)準(zhǔn)(最新標(biāo)準(zhǔn)已經(jīng)分為6個(gè)了),對(duì)于 HTTP 協(xié)議來(lái)說(shuō)味廊,header 只能是 ASCII蒸甜,不會(huì)遇到也沒(méi)有中文的問(wèn)題,但為了支持下載余佛,它借用了 MIME 的 Content-Disposition header柠新。

在早期,HTTP 協(xié)議建議使用 rfc2047 標(biāo)準(zhǔn)(查看上圖衙熔,很早的一個(gè) MIME 標(biāo)準(zhǔn))登颓,但瀏覽器沒(méi)有按照該標(biāo)準(zhǔn)執(zhí)行,這個(gè)時(shí)候出現(xiàn)了混亂红氯。

If a character set other than ISO-8859-1 is used, it MUST be encoded in the warn-text using the method described in RFC 2047

不同瀏覽器為了解決亂碼問(wèn)題框咙,提出了很多不一致的解決方案,比如對(duì) Content-Disposition 中的 filename 值進(jìn)行 base64 編碼或百分號(hào)編碼痢甘。

這個(gè)時(shí)候可苦了程序員喇嘱,為了適應(yīng)不同類(lèi)型的瀏覽器,他們需要判斷 UA塞栅,針對(duì)不同 UA 輸出不同的 Content-Disposition者铜,現(xiàn)在我們公司很多代碼也是這樣的編寫(xiě)方式。

那么有沒(méi)有好的解決方案呢放椰,rfc5987 發(fā)布了(基于 rfc2231作烟、rfc2183),它嚴(yán)格定義了在 HTTP 協(xié)議中如何標(biāo)準(zhǔn)化編碼 header 頭砾医;進(jìn)一步 HTTP 定義了 rfc6266拿撩,它進(jìn)一步標(biāo)準(zhǔn)化了在 HTTP 中如何編碼 Content-Disposition,可以認(rèn)為 rfc5987 和 rfc6266 是一樣的如蚜。

RFC 1806 , from which the often implemented Content-Disposition header in HTTP is derived, has a number of very serious security considerations. Content-Disposition is not part of the HTTP standard, but since it is widely implemented, we are documenting its use and risks for implementors.

那 rfc5987 如何定義編碼標(biāo)注的呢压恒?建議采用 parameter*=charset'lang'value 的格式影暴。如下:

ext-value     = charset  "'" [ language ] "'" value-chars
charset       = "UTF-8" / "ISO-8859-1" / mime-charset
language      = <Language-Tag>
value-chars   = *( pct-encoded / attr-char )
pct-encoded   = "%" HEXDIG HEXDIG
  • charset 字符集支持 ASCII 和 UTF-8
  • language 可以為空
  • 值編碼,采用百分號(hào)編碼
  • 當(dāng) parameter 和 parameter* 同時(shí)出現(xiàn)在 HTTP 頭中時(shí)探赫,瀏覽器應(yīng)當(dāng)使用后者型宙。

對(duì)應(yīng)到 Content-Disposition,就是輸出下面的頭:

Content-Disposition: attachment; filename="%E6%B3%95%E9%99%A2%E9%A1%B9%E7%9B%AE-%E7%B3%BB%E7%BB%9F%E6%94%AF%E6%8C%81.txt"; filename*=UTF-8''%22UTF-8%27%27%25E6%25B3%2595%25E9%2599%25A2%25E9%25A1%25B9%25E7%259B%25AE-%25E7%25B3%25BB%25E7%25BB%259F%25E6%2594%25AF%25E6%258C%2581.txt%22

新標(biāo)準(zhǔn)就是使用 filename* 代替 filename 參數(shù)伦吠,注意 UTF-8'' 后面的文件名使用百分號(hào)編碼(在 PHP 中使用 rawurlencode() 函數(shù))妆兑。為了兼容老的瀏覽器,同時(shí)也建議包含 filename 參數(shù)毛仪,對(duì)于這些老的瀏覽器來(lái)說(shuō)箭跳,會(huì)忽略 filename* 這個(gè)“不標(biāo)準(zhǔn)的” field。而如果 filename 和 filename* 同時(shí)出現(xiàn)潭千,較新的瀏覽器會(huì)忽略 filename 參數(shù)。

不過(guò)借尿,就我的測(cè)試刨晴,在 IE 瀏覽器中,還是要根據(jù) UA 輸出只包含 filename field 的參數(shù)路翻,光輸出 filename* field 沒(méi)用狈癞。

以下就是完整的示例:

$ua = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
$filename = rawurlencode($filename);

if (stripos($ua, 'msie') !== false || (stripos($ua, 'rv:11') !== false )) {
  header('Content-Disposition: attachment; filename="'.$filename.'"');
} else {
  header('Content-Disposition: attachment; filename="'.$filename.'";' . 'filename*="UTF-8\'\'' .  $filename . '"' );
} 

建議閱讀:


推薦大家關(guān)注我的公眾號(hào)(ID:yudadanwx茂契,虞大膽的嘰嘰喳喳)和我的書(shū)《深入淺出HTTPS:從原理到實(shí)戰(zhàn)》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝶桶,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子掉冶,更是在濱河造成了極大的恐慌真竖,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厌小,死亡現(xiàn)場(chǎng)離奇詭異恢共,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)璧亚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)讨韭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人癣蟋,你說(shuō)我怎么就攤上這事透硝。” “怎么了疯搅?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵濒生,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我秉撇,道長(zhǎng)甜攀,這世上最難降的妖魔是什么秋泄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮规阀,結(jié)果婚禮上恒序,老公的妹妹穿的比我還像新娘。我一直安慰自己谁撼,他們只是感情好歧胁,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著厉碟,像睡著了一般喊巍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上箍鼓,一...
    開(kāi)封第一講書(shū)人閱讀 49,821評(píng)論 1 290
  • 那天崭参,我揣著相機(jī)與錄音,去河邊找鬼款咖。 笑死何暮,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铐殃。 我是一名探鬼主播海洼,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼富腊!你這毒婦竟也來(lái)了坏逢?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤赘被,失蹤者是張志新(化名)和其女友劉穎是整,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體帘腹,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贰盗,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了阳欲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片舵盈。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情斥废,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布赴蝇,位于F島的核電站,受9級(jí)特大地震影響巢掺,放射性物質(zhì)發(fā)生泄漏句伶。R本人自食惡果不足惜劲蜻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望考余。 院中可真熱鬧先嬉,春花似錦、人聲如沸楚堤。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)身冬。三九已至衅胀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間酥筝,已是汗流浹背滚躯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嘿歌,地道東北人哀九。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像搅幅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呼胚,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容