我們有個內部HipChat Server,疑似在升級後行為改變。以往在聊天室上傳圖檔,點選其連結 httqs://hipchat-server/files/x/x/xxxx/xxxx.png 可直接顯示圖檔內容,升級後則變成點選後一律先下載到本機才能開啟。透過Chrome F12 Dev Tools,還觀察到以下訊息:
Resource interpreted as Document but transferred with MIME type image/png: "httqs://hipchat-server/files/1/3/KN2SPYfn4GfsvJh/upload.png".

使用Fiddler側錄封包,發現問題關鍵。HipChat Server在回傳PNG圖檔時,雖然Content-Type為"image/png",但多了一條Header: Content-Disposition: attachment

依據RFC 6266

   If the disposition type matches "attachment" (case-insensitively),
   this indicates that the recipient should prompt the user to save the
   response locally, rather than process it normally (as per its media
   type).

   On the other hand, if it matches "inline" (case-insensitively), this
   implies default processing.  Therefore, the disposition type "inline"
   is only useful when it is augmented with additional parameters, such
   as the filename (see below).

   Unknown or unhandled disposition types SHOULD be handled by
   recipients the same way as "attachment"

當Header指定Content-Disposition: attachment時,瀏覽器需提示使用者儲存傳回內容,忽略其原有Content-Type之處理程序,這應是HipChat改版後新增Content-Disposition: attachment Header,導致瀏覽器的行為改變。一般來說,指定檔案下載時,多半會搭配Content-Type: application/octet-stream,HipChat傳回結果卻是Content-Disposition: attachement配上Content-Type: image/png,應是Chrome「Resource interpreted as Document but transferred with MIME type image/png」警示訊息的由來。

寫了一小段程式驗證這點。在以下ASP.NET網頁,我使用GDI+動態產生圖檔並傳回png,當Request["att"]傳入檔名參數,則再加上Content-Disposition: attachment Header並指定檔名。

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Drawing" %>
<%@ Import Namespace="System.Drawing.Imaging" %>
<script runat="server">
    void Page_Load(object sender, EventArgs e)
    {
        Bitmap bmp = new Bitmap(120, 35);
        Graphics g = Graphics.FromImage(bmp);
        Brush b = new SolidBrush(ColorTranslator.FromHtml("#444"));
        g.FillRectangle(b, 0, 0, bmp.Width, bmp.Height);
        b = new SolidBrush(Color.Orange);
        Font f = new Font("Courier New", 12);
        g.DrawString("darkthread", f, b, 6, 6);
        Response.ContentType = "image/png";
        //依參數指示加上附檔Header及檔名
        string attName = Request["att"];
        if (!string.IsNullOrEmpty(attName))
        {
            Response.AddHeader("Content-Disposition",
                "attachment;filename=" + attName);
        }
        bmp.Save(Response.OutputStream, ImageFormat.Png);
        Response.End();
    }
</script>

實驗結果如下,加上att參數,原本顯示png圖檔的動作就會變成下載圖檔,得證。

此一行為改變導致查看圖檔的步驟變多,很不方便,但又無法糾正HipChat的行。同事找到一個解決方案,在瀏覽器設定「一律開啟這類檔案」,則下載後會立即啟動圖檔檢視工具開啟圖檔,也算一次到位,但無法解決每次檢視會在下載資料夾殘留檔案的問題(而且是看一次多產生一個檔案)。

最後,我想到的解決方案是寫一個簡單Proxy,能接受httq://another-server/proxy/files/1/3/KN2SPYfn4GfsvJh/upload.png呼叫,轉手向HipChat取得該圖檔,傳回瀏覽器時只指定Content-Type而沒有Content-Disposition,就能直接顯示圖檔內容;再配合Chrome的URL轉換外掛(Switchroo Redirector),每次要開啟HipChat附檔時自動轉址改由Proxy間接取檔,總算解決惱人問題。


Comments

Be the first to post a comment

Post a comment