在 Web 中展示圖片

Web 支援使用標準的 Image 元件來展示圖片。然而,Web 瀏覽器和手機以及桌面平台相比,在處理圖片上會有一定的侷限性,因為它需要以安全的方式執行未信任的程式碼。本頁面內容解釋了這些限制,並提供一些解決方法。

背景

本節概括了 Flutter Web 中可用的技術,下述的解決方案都基於此。

Images in Flutter

Flutter 提供了 Image 元件以及底層的 dart:ui/Image 類來渲染圖片。 Image 元件的功能足夠滿足大部分使用場景。 dart:ui/Image 類可用於需要精細控制圖片的場景。

Web 中的圖片

Web 提供了多種展示圖片的方式,下面是幾種常見的:

每種方式都有各自的優缺點。例如:內建的元素和其他 HTML 元素完美契合,並且自帶瀏覽器快取、內建圖片最佳化和記憶體管理的優勢。這使得你可以安全地展示任意來源的圖片(超越了下節中的 CORS)。當圖片必須和 <canvas> 元素中渲染的其他內容適配時,drawImage 更合適。當 CORS 政策允許時,你也可以獲取圖片尺寸的控制權,讀取畫素資訊用於進一步處理。最後,WebGL 給予了你最大限度的圖片控制權。你不僅可以讀取畫素資訊、應用自訂的圖片演算法,還可以使用 GLSL 實現硬體加速。

跨域資源共享 (CORS)

CORS 是一種瀏覽器用來控制一個站點如何獲取另一個站點的資源的機制。預設情況下,一個網站不允許使用 XHR 或者 fetch 的方式向另一個站點發送 HTTP 請求。這樣可以阻止另一個站點代表使用者執行指令碼,以及無需許可權就可以獲取另一個站點的資源。

當使用 <img><picture> 或者 <canvas> 時,如果瀏覽器發現圖片來源於另一個站點,且 CORS 政策不允許存取資料時,瀏覽器會自動阻止資訊的存取許可權。

WebGL 需要存取圖片資訊以用於渲染圖片。因此要想使用 WebGL 渲染圖片,該圖片的來源服務所在的域名必須配置有效且可用的 CORS 策略。

Web 中的 Flutter 渲染器

Flutter 在 Web 中提供了兩種可供選擇的渲染器:

  • HTML:該渲染器使用了 HTML、CSS、Canvas 2D 和 SVG 組合的方式渲染 UI。使用 <img> 元素渲染圖片。

  • CanvasKit:該渲染器使用了 WebGL 渲染 UI,因此需要存取圖片的畫素資訊。

因為 HTML 渲染器使用了 <img> 元素,所以可以展示任意來源的圖片。但是,這也給你帶來了以下限制:

  • Image.toByteData 的支援有限。

  • 不支援 OffsetLayer.toImageScene.toImage

  • 無法獲取動畫中的幀資訊(Codec.getNextFrameframeCount 永遠為 1,repetitionCount 永遠為 0)。

  • 不支援 ImageShader

  • 應用於圖片的著色器效果的支援有限。

  • 不可控制圖片記憶體(Image.dispose 無效)。記憶體由瀏覽器自行控制。

CanvasKit 完全實現了 Flutter 中的圖片 API。但它需要存取圖片的畫素資訊,因此受制於 CORS 政策。

解決方案

記憶體、asset 和同源網路圖片

如果圖片在應用記憶體中有編碼後的位元組資訊、或者以 asset 的方式提供、或者和應用儲存在同一伺服器上(也就是同源),則不需要做額外工作。圖片既可以在 HTML 也可以在 CanvasKit 模式下,使用 Image.memoryImage.assetImage.network 來展示。

跨域圖片

HTML 渲染器可以載入跨域圖片,無需額外配置。

CanvasKit 需要應用獲取編碼後的圖片的位元組資訊。有多種獲取方式,如下文所述。

在支援 CORS 的 CDN 中部署你的圖片

通常,可以配置內容分發網路 (CDN) 來自訂哪些域名可以存取你的內容。例如:Firebase 站點託管允許在 firebase.json 檔案中, 指定一個自訂的 Access-Control-Allow-Origin 頭。

沒有圖片伺服器控制權?使用一個 CORS 代理

如果無法從你的應用層面去配置圖片伺服器的 CORS,你依然可以透過另一個伺服器代理請求,從而載入圖片。這要求中轉伺服器對圖片載入有充分的存取權。

此方法適用於源圖片伺服器公開提供了圖片,卻沒有正確配置 CORS 頭的情況。

例子:

在平臺視圖中使用 <img>

Flutter 支援在應用中使用 HtmlElementView 嵌入 HTML。透過它可以建立一個 <img> 元素來渲染另一個域名的圖片。但是,一定要記住,此方法也附帶了「Web 中的 Flutter 渲染器」一節中提到的限制。

就目前而言,在 CanvasKit 渲染器中過多地使用 HTML 元素,可能較為影響效能。如果圖片和非圖片內容交替出現, Flutter 需要在 <img> 元素之間建立額外的 WebGL 上下文。如果你的應用需要一次性在同一螢幕中展示大量的圖片,請考慮使用 HTML 渲染器替代 CanvasKit。