Chrome在處理檔案下載引發Resource interpreted警告

今天同事回報在網站的一個稀鬆平常的PDF檔案下載,在檔案正確下載到本地端的情況下,Google Chrome瀏覽器的DevTools(F12)中卻回報了下列字眼。

Resource interpreted as Document but transferred with MIME type application/octet-stream

由於該網站的檔案下載是由一個公用的類別統一去操作HttpContext,所以當下的直覺是Google Chrome也管太寬了,連我不老實的輸出MIME也要管,當我把System.Web.MimeMapping.GetMimeMapping動態判斷MIME的程式碼加入時,發現根本沒有用,檔案依然是正確下載了,但Google Chrome吐給我的警告也更WTF了!

Response Headers:
-----
Content-Disposition: attachment; filename="TEST.pdf"
Content-Type: application/pdf

Google Chrome DevTools (F12)
-----
Resource interpreted as Document but transferred with MIME type application/pdf

WTF?我的MIME都正確了還吐出警告?

開始瘋狂搜尋網路的討論,發現有這樣困擾的工程師還真不少,在各大論壇中都有人提出,但給的答案其實都無濟於事,有的人說要補上Content-Disposition,有的人說要補上Content-Length,拜託,這個我本來就都有了好嗎?

Google Chrome瀏覽器跟你警告的中譯是:資源被理解成一個文件,但是傳輸過來的MIME卻跟我說是一個PDF。然而,PDF就是文件吧?這個警告說明,再怎麼想也是正確的啊!

引發這個警告的「可能」原因

無意間在Chrome的Request Headers看到這行:

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8

所以可能Google Chrome瀏覽器其實想要跟你講:我「預計收到」的是一個我想要的文件,你卻丟一個PDF檔案叫我去儲存,現在是三小情況?

突然一閃而過這個可能性時,心中的OS是,我正確的描述檔案MIME,並請你以attachment的型態去把檔案儲存也有事?

讓這個警告不再出現的方法

修改下載標籤

在a標籤的後面加上「download」屬性,叫瀏覽器用download的方式去處理這個連結,這個方法對Chrome有用,但對瀏覽器的相容性卻很有風險性,自己上Can I Use查看看便知到後果。

況且,修改全網站的a標籤的寫法,也不是一件好味道的事情。

採用公認標準寫法

基本上所謂的標準寫法,就是只打出Content-Type,而不在去輸出其他HTTP標頭,讓瀏覽器自己去判斷要怎麼處理這個MIME,這樣的做法會形成一些後果...

  1. 如果這個檔案是瀏覽器可以處理的檔案的話,那就不會出現警告。例如瀏覽器看到PDF檔案會用瀏覽器內建程式打開,你要嘛直接在裡面看,要嘛去點擊內建PDF瀏覽程式的下載按鈕將檔案下載。但你的PDF檔案名稱會以網址上面的最後連結文字(通常是一組UUID)當作是檔名。
  2. 承上,如果你想要自訂檔名而非以URL當作存檔名稱,那麼可以使用「Content-Disposition: inline; filename="TEST.pdf"」來解決。
  3. 遇到瀏覽器無法處理的MIME檔案,那警告還是會出現。例如ZIP檔案會引發自動下載,連Google自己的網站的ZIP下載也會發出類似的警告,詳見下方:
Resource interpreted as Document but transferred with MIME type application/octet-stream

永遠儲存檔案到本機寫法

基本上我自己是很討厭使用瀏覽器內建的PDF瀏覽器,或者是在瀏覽器中嵌入WORD之類的Viewer在裡面進行瀏覽操作行為,因此我對檔案下載的觀點就是,下載到我的本地端就好,你管我要怎麼使用?

在ASP.NET中的答案就是,不要輸出任何「Content-Type」,你Chrome不想聽我也不想講啊!只要載明「Content-Disposition: attachment; filename="TEST.pdf"」就好。在ASP.NET的機制下發現你沒有輸出Content-Type,就會自動幫你補上「Content-Type: text/html」,但在修補的過程中,你原本自行運算輸出的Content-Length也消失無蹤了,這意味著你喪失了檔案下載的進度觀測功能。

非ASP.NET的解法,也是自己直接硬輸出成「Content-Type: text/html」或「Content-Type: application/xhtml+xml」,讓瀏覽器認為這是text/html(符合預期),但瀏覽器又受到Content-Disposition控制,故不解釋它且直接儲存。

這下,DevTools(F12)就不會有任何警告啦!

後記

這篇文章只是在探討為何會發生這種錯誤,但最終我選擇做自己,我把所有正確的資訊都餵給瀏覽器,你Chrome要警告就警告吧,反正所有的下載功能也都是正常運行無誤。

GoogleChrome HttpHeader Content-Type Content-Disposition ResourceInterpretedAsDocument ButTransferredWithMimeType