Cloudfront 根据请求标头提供 png/jpg 与 webp

问题描述 投票:0回答:2

我在 S3 前面有 Cloudfront 提供图像(png 和 jpg)。 我将所有 webp 格式的 png 和 jpg 图像放在同一目录中,扩展名为 .webp。例如:

  • png: /path/to/file.png
  • webp:/path/to/file.png.webp

我想动态提供 webp 文件而不更改标记。 由于浏览器通过 Accept 标头标记 webp 支持,我需要做的是:如果用户支持 webp(通过 Accept 标头)Cloudfront 将提取 webp 版本(filename.png.webp),如果没有,它应该提供原始文件(文件名.png)

这可以实现吗?

amazon-s3 amazon-cloudfront webp
2个回答
0
投票

使 Cloudfront 为不同的资源提供服务很容易(当您已经完成几次时),但我担心的是发出请求的实体(即浏览器)和(代理等)之间可能的缓存元素是否期望在其上具有不同的媒体类型相同的请求 URI。但这有点超出了你的问题。我相信处理这个问题的通常的方法是使用一个元素,浏览器可以自由地从不同的媒体类型中选择图像,如下所示:

<picture>
  <source type="image/svg+xml" srcset="pyramid.svg" />
  <source type="image/webp" srcset="pyramid.webp" />
  <img
    src="pyramid.png"
    alt="regular pyramid built from four equilateral triangles" />
</picture>

但是,如果您仍然想为同一个 URL 从 Cloufront 提供不同的内容,您可以这样做:

Cloudfront 有 4 个不同的点,您可以在其中注入 lamdba 函数以进行请求操作 (Lambda@Edge)。

对于您的用例,我们需要在源请求位置创建一个 Lambda@Edge 函数,然后将此函数与您的 Cloudfront 分配相关联。

下面是来自 AWS 文档 的示例,它查看设备类型并执行 URL 操作。对于您的用例,可以通过查看“接受”标题来完成类似的操作。

'use strict';

/* This is an origin request function */
exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    /*
     * Serve different versions of an object based on the device type.
     * NOTE: 1. You must configure your distribution to cache based on the
     *          CloudFront-Is-*-Viewer headers. For more information, see
     *          the following documentation:
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-selected-headers
     *          https://docs.aws.amazon.com/console/cloudfront/cache-on-device-type
     *       2. CloudFront adds the CloudFront-Is-*-Viewer headers after the viewer
     *          request event. To use this example, you must create a trigger for the
     *          origin request event.
     */

    const desktopPath = '/desktop';
    const mobilePath = '/mobile';
    const tabletPath = '/tablet';
    const smarttvPath = '/smarttv';

    if (headers['cloudfront-is-desktop-viewer']
        && headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
        request.uri = desktopPath + request.uri;
    } else if (headers['cloudfront-is-mobile-viewer']
               && headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
        request.uri = mobilePath + request.uri;
    } else if (headers['cloudfront-is-tablet-viewer']
               && headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
        request.uri = tabletPath + request.uri;
    } else if (headers['cloudfront-is-smarttv-viewer']
               && headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
        request.uri = smarttvPath + request.uri;
    }
    console.log(`Request uri set to "${request.uri}"`);

    callback(null, request);
};

接下来,您需要告诉 Cloudfront 您想要使用 Accept 标头作为缓存键的一部分(否则 Cloudfront 只会执行您的 Origin Request lambda 一次,并且不会将此标头公开给您的函数)。 现在,您可以使用缓存和源请求策略来执行此操作。或者使用旧设置(在 Cloudfront 分发设置下编辑行为),例如:

这里值得注意的是,如果由于 Accept 标头的不同变体而导致缓存命中率较低,则需要对其进行预处理/清理。我的方法是使用针对每个请求执行的 Viewer Request Lamdba。然后,这个新的 Lambda 将检查 Accept 标头是否支持 Webp,然后将一个新标头添加到传递到上面的原始请求的请求中。这样,原始请求就可以缓存这个新标头(它只有两个不同的可能值)

还需要更多配置/设置,例如让 Lamdba 运行的 IAM 策略等,但是有很多很棒的材料可以引导您完成这些步骤。 也许从这里开始?


0
投票

我有同样的情况,如果浏览器兼容webp,我想返回webp,你找到解决方案了吗?

© www.soinside.com 2019 - 2024. All rights reserved.