跳至主要內容
PHUA MEDIA

請求明明回 200,圖片卻還是破圖:一張 87 MB 的 JPEG,解碼後要喫掉近 5 GB 記憶體

刊登:(台北時間) 閱讀約 4 分鐘

掘金一篇2026年10月16日的分享拆解「HTTP 200卻顯示破圖」之謎,一張87 MB的地圖JPEG解碼後要喫掉近5 GB記憶體,本文以問答整理來龍去脈。

先把時間與背景補上。2026 年 10 月 16 日,開發者 chenCode 在掘金發文,記錄了自己在專案裡踩到的一個坑:成果文件預覽功能用 Element Plus 的 ElImageViewer 開圖,大多數圖片正常,唯獨幾張地圖成果始終打不開。Chrome DevTools 裡狀態是 200 OK,檔案也完整下載了,頁面上卻只跳出「圖片載入失敗」。

這篇文章以問答方式整理該文的排查思路,看「200 OK」這四個字為什麼會騙人。

Q1:請求都成功了,為什麼圖還是顯示不出來?

瀏覽器展示一張網路圖片,中間其實有好幾道關卡。先發 HTTP 請求、下載壓縮資料,接著解析圖片格式、把壓縮資料解碼成記憶體裡的像素位圖,最後上傳給 GPU 繪製到頁面。

Network 面板上的 200 OK,只能證明前面下載階段成功了,不代表後面的解碼與繪製也過關。所以破圖圖示和綠色的 200 同時出現,兩者並不矛盾。

Q2:那張打不開的圖,到底多大?

文中給出的數字很具體。這張 JPEG 檔案約 87 MB,尺寸是 34253 × 37794,換算下來總像素約 12.95 億。

關鍵在於:87 MB 只是磁碟和傳輸時的壓縮體積。瀏覽器要顯示它,得先解成未壓縮的像素資料。若按 RGBA 四通道、每像素 4 位元組計算,34253 × 37794 × 4 約等於 4.82 GiB。一張看起來 87 MB 的圖,解碼後要喫掉接近 5 GB 記憶體,而且這還沒算壓縮資料本身、解碼過程的暫存緩衝區、GPU 紋理副本,以及縮放旋轉時產生的新位圖。

原文的結論是:載入失敗是一個很合理的結果。

Q3:是 Element Plus 元件限制了檔案大小嗎?

很容易先懷疑元件。但該文查看 ElImageViewer 的實作後發現,它最終仍是用普通的 <img> 標籤載入圖片,元件本身提供的是遮罩、縮放、旋轉、拖拽這些互動,並沒有設定幾十 MB 或幾百 MB 的檔案上限。

真正失敗的是瀏覽器底層的圖片解碼。而且瀏覽器的可用上限並不是一個統一的「檔案大小」數字,會受圖片寬高、像素總量、色彩通道與位深、作業系統與瀏覽器實作、渲染程序的可用記憶體、GPU 支援的最大紋理尺寸等因素影響。

所以「小於 100 MB 就一定打得開」這種規則並不成立。一張壓縮率很高、但尺寸極大的圖,可能比檔案更大、像素更少的圖更容易出事。

Q4:遇到這種問題怎麼定位?

該文建議的順序是先看 Network,別只看頁面破圖。重點確認三個欄位:Status Code 是否為 200、Content-Type 是否真的是 image/jpeg、Content-Length 是否符合預期(該案例為 91,376,018 位元組)。

狀態碼不是 200,先排查鑑權、路徑、跨域、代理或靜態資源映射。狀態碼是 200,也要檢查 Content-Type,有些介面即使回的是登入頁或 JSON 錯誤訊息,狀態碼照樣是 200。

第二步是監聽 <img> 的 load 與 error 事件。用 new Image() 建立物件,在 onload 裡印出 naturalWidth 與 naturalHeight,在 onerror 裡就能確認一件事:圖片下載成功,但解碼或渲染失敗。這一步等於把問題從網路層剝開,直指解碼階段。

Q5:這件事對一般使用者的啟示是什麼?

多數人不會碰到 12.95 億像素的地圖成果圖,但「下載完成」和「看得見」之間有距離,這個概念其實很日常。微信傳大圖偶爾轉圈、看圖軟體開航拍圖卡頓,背後都是同一類解碼成本問題。

對開發者來說,這篇分享提醒了一個排查習慣:別讓 Network 面板的綠色 200 誤導判斷。至於大型圖檔的預覽方案,先前另一篇掘金分享提過把 DWG 預先轉成靜態資源、讓使用者開網頁直接看圖的預渲染看圖方案,思路正好與「別讓瀏覽器現場硬解超大檔案」同一個方向。

一個 200,一道解碼關卡,近 5 GB 的記憶體帳單。下次看到破圖但請求正常,先想想那張圖解開之後有多大。

主題: 消費生活

標籤: #超大圖片載入失敗#瀏覽器解碼#掘金技術分享