在 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。