通常HTTP消息包括客戶機(jī)向服務(wù)器的請(qǐng)求消息和服務(wù)器向客戶機(jī)的響應(yīng)消息,今天來講解下正確處理下載文件時(shí)HTTP頭的編碼問題,需要的朋友可以參考下" />

最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java如何正確處理下載文件時(shí)HTTP頭的編碼問題

 更新時(shí)間:2023年07月03日 11:43:58   作者:今夜無風(fēng)亦無雨  
這篇文章主要介紹了Java如何正確處理下載文件時(shí)HTTP頭的編碼問題,
通常HTTP消息包括客戶機(jī)向服務(wù)器的請(qǐng)求消息和服務(wù)器向客戶機(jī)的響應(yīng)消息,今天來講解下正確處理下載文件時(shí)HTTP頭的編碼問題,需要的朋友可以參考下

最近在做項(xiàng)目時(shí)遇到了一個(gè) case :需要實(shí)現(xiàn)一個(gè)強(qiáng)制下載功能(即強(qiáng)制彈出下載對(duì)話框),并且文件名必須保持和用戶之前上傳時(shí)相同(可能包含非 ASCII 字符)。

前一個(gè)需求很容易實(shí)現(xiàn):使用 HTTPHeader的Content-Disposition: attachment即可,還可以配合Content-Type: application/octet-stream來確保萬無一失。而后一個(gè)需求就比較蛋疼了,牽扯到 Header 的編碼問題(文件名是作為 filename 參數(shù)放在 Content-Disposition 里面的)。眾所周知, HTTP Header 中的 Content-Type 可以指定內(nèi)容的編碼,可 Header 本身的編碼又該如何制定?甚至, Header 究竟是否允許非 ASCII 編碼呢?

如果放任編碼問題不管,那么恭喜你,你一定會(huì)遇到在某個(gè)系統(tǒng)及瀏覽器下下載文件時(shí)文件名亂碼的情況。如果你嘗試搜索解決,那么再一次恭喜你,你會(huì)找到一堆自相矛盾的解決方案(我可以負(fù)責(zé)任地告訴你,其中的99%都是不符合標(biāo)準(zhǔn)的 trick 罷了)。讓我們來看看到底應(yīng)該如何優(yōu)雅完美地解決這個(gè)問題吧!

為了探索這個(gè)問題,我走了不少?gòu)澛?。從自己嘗試,到 Google 、百度(分別嘗試過中英文搜索),再到閱讀 Discuz 等經(jīng)典項(xiàng)目的源碼,眾說紛紜、莫衷一是。最后我才想到回歸 RFC ,從標(biāo)準(zhǔn)文檔中找辦法,果然有所收獲。由于探究過程實(shí)在太曲折,我就先把標(biāo)準(zhǔn)做法寫下來。

應(yīng)該這樣設(shè)置 Content-Disposition :

Content-Disposition: attachment;
                     filename="$encoded_fname";
                     filename*=utf-8''$encoded_fname

其中,$encoded_fname指的是將 UTF-8 編碼的原始文件名按照RFC 3986進(jìn)行百分號(hào) urlencode 后得到的( PHP 中使用rawurlencode()函數(shù))。這幾行也可以合并為一行,推薦使用一個(gè)空格隔開。

另外,為了兼容 IE6 ,請(qǐng)保證原始文件名必須包含英文擴(kuò)展名!

好了,接下來我們來看看為什么要這么做以及為什么能這么做。

首先,根據(jù) HTTP 1.1 協(xié)議規(guī)范(RFC 2616 Section 4), HTTP 消息格式其實(shí)是基于古老的 ARPA INTERNET TEXT MESSAGES (RFC 822 Section 3),根據(jù)其規(guī)定,消息只能是 ASCII 編碼的。RFC 2616 Section 2.2又一次強(qiáng)調(diào), TEXT 中若要使用其他字符集,必須使用RFC 2047的規(guī)則將字符串編碼為 ASCII 碼(事實(shí)上這個(gè)規(guī)則原本是針對(duì) MIME 的擴(kuò)展,使用的是 base64 編碼,格式與百分號(hào)編碼有很大不同)。總而言之,按照標(biāo)準(zhǔn), HTTP Header 中的文本數(shù)據(jù)必須是 ASCII 編碼的。

filename="TEXT"
;這是 RFC 2616 標(biāo)準(zhǔn),TEXT必須是 ASCII 字符且被認(rèn)為就是“原文”
filename*=charset'lang'encoded-text
;這是按照 RFC 2047 擴(kuò)展后的,注意格式上的細(xì)微區(qū)別,采用 base64 編碼(編碼結(jié)果也是 ASCII 字符)

然而,事實(shí)上在1999年 HTTP 1.1 標(biāo)準(zhǔn)推出之時(shí), Content-Dispostion 這個(gè) Header 尚不是正式標(biāo)準(zhǔn)的一部分,只不過是因?yàn)楸粡V泛使用而從 MIME 標(biāo)準(zhǔn)中直接借用過來了而已(RFC 2616 Section 19.5.1)。因而幾乎沒有瀏覽器去支持 Content-Disposition 的多語言編碼特性這樣一個(gè)“擴(kuò)展特性的擴(kuò)展特性”(事實(shí)上, HTTP 1.1 草案中建議的使用 RFC 2047 來進(jìn)行多語言編碼的特性從未被主流瀏覽器支持過)。

可是這個(gè)問題卻的確是現(xiàn)實(shí)需要的,所以瀏覽器就各自想出了一些辦法:

  • IE支持兩種格式的混合版:filename="encoded_text"(這里采用的是百分號(hào)編碼)。本來按照 RFC 2616 ,引號(hào)內(nèi)的部分應(yīng)當(dāng)直接被當(dāng)作內(nèi)容,就算它“看起來像是編碼后的字符串”;可是IE卻會(huì)“自動(dòng)”對(duì)這樣的文件名進(jìn)行解碼——前提是該文件名必須有一個(gè)不會(huì)被編碼的后綴名(即正常的英文字母后綴名)!
  • 其他一些瀏覽器則支持一種更為粗暴的方式——允許在filename="TEXT"中直接使用 UTF-8 編碼的字符串!

這兩類瀏覽器的行為是彼此互不兼容的。所以你可以判斷 UA 然后對(duì)IE使用前一種辦法,其他瀏覽器使用后一種,這樣便可以達(dá)到一般情況下能夠 just work 的效果( Discuz 就是這么做的)。不過對(duì)于 Opera 和 Safari ,這樣做可能不一定有效。

時(shí)代在進(jìn)步,2010年RFC 5987發(fā)布,正式規(guī)定了 HTTP Header 中多語言編碼的處理方式,應(yīng)當(dāng)采用類似 MIME 擴(kuò)展的parameter*=charset'lang'value的格式,但是其中 value 應(yīng)根據(jù)RFC 3986 Section 2.1使用百分號(hào)進(jìn)行編碼,并且規(guī)定瀏覽器至少應(yīng)該支持 ASCII 和 UTF-8 。隨后,2011年RFC 6266發(fā)布,正式將 Content-Disposition 納入 HTTP 標(biāo)準(zhǔn),并再次強(qiáng)調(diào)了 RFC 5987 中多語言編碼的方法,還給出了一個(gè)范例用于解決向后兼容的問題——就是我在一開始給出的例子:

Content-Disposition: attachment;
                     filename="encoded_text";
                     filename*=utf-8''encoded_text

在這個(gè)例子中,對(duì)于較新的 Firefox 、 Chrome 、 Opera 、 Safari 等瀏覽器,都支持新標(biāo)準(zhǔn)規(guī)定的 filename* ,并且會(huì)優(yōu)先使用,所以盡管 filename=”encoded_text” 不被它們支持,仍然不會(huì)有問題;至于使用 UTF-8 只是因?yàn)樗菢?biāo)準(zhǔn)中強(qiáng)制要求必須支持的。而對(duì)于舊版本的IE瀏覽器,它們無法識(shí)別后面的 filename* ,會(huì)自動(dòng)忽略并使用舊的 filename 。這樣一來就完美解決了多瀏覽器的多語言兼容問題,既不需要 UA 判斷,也符合標(biāo)準(zhǔn)。

P.S.為什么 PHP 要使用rawurlencode()函數(shù)呢?因?yàn)檫@才是真正符合 RFC 3986 的“百分號(hào)URL編碼”,只是由于歷史原因,之前先有了一個(gè)urlencode()函數(shù)用于實(shí)現(xiàn) HTTP POST 中的類似的編碼規(guī)則,故而只好用這么一個(gè)奇怪的名字。兩者的區(qū)別在于前者會(huì)把空格編碼為%20,而后者則會(huì)編碼為+號(hào)。如果使用后者,那么IE6在下載帶有空格的文件名時(shí)空格會(huì)變?yōu)榧犹?hào)。一般情況下,你是不會(huì)用到urlencode()這個(gè)函數(shù)的( Discuz 某些版本中錯(cuò)誤地使用它來進(jìn)行文件名編碼,從而導(dǎo)致空格變加號(hào)的BUG)。

到此這篇關(guān)于Java如何正確處理下載文件時(shí)HTTP頭的編碼問題的文章就介紹到這了,更多相關(guān)Java下載文件HTTP頭編碼問題內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot實(shí)現(xiàn)版本升級(jí)到2.7.18

    SpringBoot實(shí)現(xiàn)版本升級(jí)到2.7.18

    這篇文章主要介紹了SpringBoot實(shí)現(xiàn)版本升級(jí)到2.7.18全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java文本編輯器實(shí)現(xiàn)方法詳解

    Java文本編輯器實(shí)現(xiàn)方法詳解

    這篇文章主要介紹了Java文本編輯器實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了java文本編輯器結(jié)構(gòu)、原理、布局、實(shí)現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下
    2019-03-03
  • Java踩坑記錄之BigDecimal類

    Java踩坑記錄之BigDecimal類

    這篇文章主要給大家介紹了關(guān)于Java踩坑記錄之BigDecimal類的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-11-11
  • JavaSE實(shí)戰(zhàn)之酒店訂房系統(tǒng)的實(shí)現(xiàn)

    JavaSE實(shí)戰(zhàn)之酒店訂房系統(tǒng)的實(shí)現(xiàn)

    這篇文章主要為大家詳細(xì)介紹了如何利用JavaSE實(shí)現(xiàn)酒店訂房系統(tǒng),文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)JavaSE開發(fā)有一定的幫助,需要的可以參考一下
    2022-07-07
  • java 中線程等待與通知的實(shí)現(xiàn)

    java 中線程等待與通知的實(shí)現(xiàn)

    這篇文章主要介紹了java 中線程等待與通知的實(shí)現(xiàn)的相關(guān)資料,希望通過本文能幫助到大家,讓大家掌握這樣的功能,需要的朋友可以參考下
    2017-09-09
  • 使用Java構(gòu)造和解析Json數(shù)據(jù)的兩種方法(詳解二)

    使用Java構(gòu)造和解析Json數(shù)據(jù)的兩種方法(詳解二)

    這篇文章主要介紹了使用Java構(gòu)造和解析Json數(shù)據(jù)的兩種方法(詳解二)的相關(guān)資料,需要的朋友可以參考下
    2016-03-03
  • java Servlet 實(shí)現(xiàn)動(dòng)態(tài)驗(yàn)證碼圖片示例

    java Servlet 實(shí)現(xiàn)動(dòng)態(tài)驗(yàn)證碼圖片示例

    這篇文章主要介紹了java Servlet 實(shí)現(xiàn)動(dòng)態(tài)驗(yàn)證碼圖片示例的資料,這里整理了詳細(xì)的代碼,有需要的小伙伴可以參考下。
    2017-02-02
  • Java和Scala集合間的相互轉(zhuǎn)換方式

    Java和Scala集合間的相互轉(zhuǎn)換方式

    這篇文章主要介紹了Java和Scala集合間的相互轉(zhuǎn)換方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • java反射總結(jié)實(shí)例詳解

    java反射總結(jié)實(shí)例詳解

    這篇文章主要結(jié)合實(shí)例形式分析了介紹了java基于反射得到對(duì)象屬性值的方法,Class類,基本數(shù)據(jù)類型,類的反射等,需要的朋友可以參考下
    2017-04-04
  • 利用idea快速搭建一個(gè)spring-cloud(圖文)

    利用idea快速搭建一個(gè)spring-cloud(圖文)

    本文主要介紹了idea快速搭建一個(gè)spring-cloud,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評(píng)論

永善县| 资中县| 乌拉特中旗| 大埔区| 霞浦县| 阿尔山市| 灵川县| 呼玛县| 河曲县| 天水市| 大港区| 西盟| 巴东县| 自贡市| 紫云| 东乌珠穆沁旗| 安岳县| 赣州市| 榕江县| 米脂县| 五莲县| 临汾市| 巴中市| 上高县| 丘北县| 墨竹工卡县| 安平县| 泸西县| 呼图壁县| 贵定县| 芒康县| 汕尾市| 芜湖县| 舒兰市| 大竹县| 太康县| 景谷| 历史| 修武县| 靖州| 集安市|