web開發中,如何讓瀏覽器下載檔案?
上個星期,一個對外的專案遇到瀏覽器下載附件錯誤的問題,簡單的研究了下,涉及了二個問題:一個是通過程式設計如何讓瀏覽器支援檔案下載,第二個問題是如果附件是中文名稱,如何避免檔案出現亂碼,這篇文章主要講解檔案下載的原理。
從本質上講,為了支援檔案下載,客戶端(瀏覽器)和伺服器支援特定的HTTP頭 即可,但不是所有的客戶端(瀏覽器)都按照標準 處理,比如說iPhone就無法通過瀏覽器下載檔案。
在rfc7231 中(HTTP/1.1 子協議 Semantics and Content)中已經說明 Content-Disposition 頭並不是 HTTP 協議中的標準頭,但 Content-Disposition 頭在 HTTP 應用中有一定的應用場景,所以rfc6266 詳細描述了這個頭部在 HTTP 中的使用標準,確切的說在 HTTP 應用中,這個頭部稱為 response header。
那麼是否意味著 Content-Disposition 這個頭部還有其他用途?是的,最早這個頭在郵件應用中使用的比較多,屬於 MIME 的一部分,Content-Disposition header(不是 response header)定義在 rfc2183 上,我在寫《你真的知道網頁上傳檔案背後的原理嗎?》文章時,對於 MIME 中如何使用 Content-Disposition 有所涉及。
我參考 CodeIgniter PHP 框架,研究了檔案下載的原理,實際上就是輸出一些 HTTP 頭部,如下圖:
大部分頭部在常規的 HTTP 應用中(比如頁面和介面)很常見,比如 Content-Length 表示附件的大小(在PHP中使用 strlen()算對嗎?);Cache-Control和Expires控制快取頭,對於下載附件來說,就是強制獲取最新內容;Content-Type表示附件的型別,不過如果你統一使用 application/octet-stream 也沒有問題,瀏覽器照樣能夠下載;application/octet-stream 頭部代表未知的應用(二進位制檔案)。
Content-Transfer-Encoding 這個頭部是第一次看見,它和 Content-Type 頭部有點類似,但主要用於傳輸。它在 multipart MIME 型別中用的比較多(如果一個實體是 multipart 型別,那麼 Content-Transfer-Encoding 的值只能是 "7bit"、"8bit"、"binary"中的一個),對應到檔案下載,這個值設定為 binary,表示允許非ASCII碼進行傳輸。可以認為 application/octet-stream 等同於 Content-Transfer-Encoding:binary 。關於這個頭部,理解的不是很好,可以參考rfc2045 (內容實在太多了)。
對於檔案下載,最重要的HTTP頭部就是 Content-Disposition,它用來表示內容如何在瀏覽器中顯示:可以是內聯(inline)呈現(對應的內容將作為頁面的一部分呈現)或者附件下載(attachment)的形式。其中 filename 引數表示附件的名稱,如果是中文,在不同的瀏覽器可能會出現亂碼(這個我下一篇會簡單說一說)。
本質上附件下載就是這麼簡單,但在 RFC 中沒有清晰的看到實現附件下載的說明。