首页 > 解决方案 > Lambda Edge - 指定的键不存在

问题描述

我正在使用 lambda edge 来处理 Sharp 的图像压缩。该代码现在可以工作,但是当我尝试添加一个新函数来解析查询参数以让用户定义压缩质量时,Lambda/Cloudfront 开始通知我密钥不存在,即使它确实存在。

用作示例的路径是:

/compress/480/uploads/1000491600869812260.jpg?quality=30

浏览器上显示的错误:

<Error>
  <Code>NoSuchKey</Code>
  <Message>The specified key does not exist.</Message>
  <Key>compress/480/uploads/1000491600869812260.jpg</Key>
  <RequestId>5KPMBD6RNETZCA3Z</RequestId>
  <HostId>
    brMd/eCi6uv9s3VIl4IRHg7FlIytNA8DkgPjGfGrej4SkUsMxuEm1YHGEEll5rydO24gecIOTtE=
  </HostId>
</Error>

来自云端的错误日志:

#Version: 1.0
#Fields: date time x-edge-location sc-bytes c-ip cs-method cs(Host) cs-uri-stem sc-status cs(Referer) cs(User-Agent) cs-uri-query cs(Cookie) x-edge-result-type x-edge-request-id x-host-header cs-protocol cs-bytes time-taken x-forwarded-for ssl-protocol ssl-cipher x-edge-response-result-type cs-protocol-version fle-status fle-encrypted-fields c-port time-to-first-byte x-edge-detailed-result-type sc-content-type sc-content-len sc-range-start sc-range-end
2021-06-09  06:06:43    ORD52-C3    689 182.253.36.23   GET d32xc09eirob59.cloudfront.net   /compress/480/uploads/1000491600869812260.jpg   404 -   Mozilla/5.0%20(Macintosh;%20Intel%20Mac%20OS%20X%2010_15_7)%20AppleWebKit/605.1.15%20(KHTML,%20like%20Gecko)%20Version/14.1.1%20Safari/605.1.15 quality=10  -   Error   FPFQE5Z-XuBeAK61KaJbNqDAbypyo3BhrH7xom7GZik--UgESIVQFw==    d32xc09eirob59.cloudfront.net   http    426 3.726   -   -   -   Error   HTTP/1.1    -   -   54708   3.726   Error   application/xml -   -   -

在下面的代码中,如果我注释调用函数以从查询参数解析质量的行(在代码中标记为“有问题的行”),代码将再次起作用。但是,从我的角度来看,代码没有任何问题,因为它是一个简单的正则表达式来获取一个值。

AWS lambda 中是否有任何限制或约束使其行为如此?我能做些什么来让它发挥作用吗?

PS 我已经尝试使用URLquerystring库来解析路径,但它总是向我显示 LambdaException 错误,因此我尝试使用正则表达式手动解析它

有问题的线路/功能:

const getQuality = (path) => {
  const match = path.match(/quality=(\d+)/)
  const quality = parseInt(match[1], 10)
  return quality
}

const quality = getQuality(path)

完整代码:

'use strict'

const AWS = require('aws-sdk')
const S3 = new AWS.S3({ signatureVersion: 'v4' })
const Sharp = require('sharp')

const BUCKET = 'some-bucket'
const QUALITY = 70

// Image types that can be handled by Sharp
const SUPPORTED_IMAGE_TYPES = ['jpg', 'jpeg', 'png', 'gif', 'webp', 'svg', 'tiff']
const JSON_CONTENT_HEADER = [{ key: 'Content-Type', value: 'application/json' }]
const WEBP_CONTENT_HEADER = [{ key: 'Content-Type', value: 'image/webp' }]

const getOriginalKey = (path) => {
  const match = path.match(/\/(\d+)\/([A-Za-z0-9_\-]+)\/([A-Za-z0-9_\-]+)\.(\w+)\??/)

  const imageWidth = parseInt(match[1], 10)
  const prefix = match[2]
  const imageName = match[3]
  const imageFormat = match[4]

  const originalKey = `${prefix}/${imageName}.${imageFormat}`
  return { originalKey, imageWidth, imageFormat }
}

const getQuality = (path) => {
  const match = path.match(/quality=(\d+)/)
  const quality = parseInt(match[1], 10)
  return quality
}

const responseUpdate = (
  response,
  status,
  statusDescription,
  body,
  contentHeader,
  bodyEncoding = undefined
) => {
  response.status = status
  response.statusDescription = statusDescription
  response.body = body
  response.headers['content-type'] = contentHeader
  if (bodyEncoding) {
    response.bodyEncoding = bodyEncoding
  }

  return response
}

exports.handler = async (event, context, callback) => {
  let { request, response } = event.Records[0].cf
  const { uri } = request
  const headers = response.headers

  console.log(JSON.stringify({ status_code: response.status, uri }))

  // NOTE: Check whether the image is present or not
  if (response.status == 404) {
    const splittedUri = uri.split('compress')

    if (splittedUri.length != 2) {
      callback(null, response)
      return
    }

    // NOTE: Parse the prefix, image name, imageWidth and format
    const path = splittedUri[1] // Read the required path (/480/uploads/123.jpg)
    const { originalKey, imageWidth, imageFormat } = getOriginalKey(path)

    if (!SUPPORTED_IMAGE_TYPES.some((type) => type == imageFormat.toLowerCase())) {
      response = responseUpdate(
        response,
        403,
        'Forbidden',
        'Unsupported image type',
        JSON_CONTENT_HEADER
      )
      callback(null, response)
      return
    }

    try {
      // NOTE: Get original image from S3
      const s3Object = await S3.getObject({ Bucket: BUCKET, Key: originalKey }).promise()

      if (s3Object.ContentLength == 0) {
        response = responseUpdate(
          response,
          404,
          'Not Found',
          'The image does not exist',
          JSON_CONTENT_HEADER
        )
        callback(null, response)
        return
      }

      // NOTE: Optimize the image
      let sharpObject = await Sharp(s3Object.Body)
      const metaData = await sharpObject.metadata()

      if (imageWidth < metaData.width) {
        sharpObject = await sharpObject.resize(imageWidth)
      }

      // >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
      // NOTE: The problematic line
      const quality = getQuality(path)
      // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

      const compressedImageBuffer = await sharpObject.webp({ quality: QUALITY }).toBuffer()

      const byteLength = Buffer.byteLength(compressedImageBuffer, 'base64')
      if (byteLength == metaData.size) {
        callback(null, response)
        return
      }

      if (byteLength >= 1046528) {
        response = responseUpdate(
          response,
          400,
          'Invalid size',
          'The size of compressed image is too big',
          JSON_CONTENT_HEADER
        )
        callback(null, response)
        return
      }

      // NOTE: Generate a binary response with an optimized image
      response = responseUpdate(
        response,
        200,
        'OK',
        compressedImageBuffer.toString('base64'),
        WEBP_CONTENT_HEADER,
        'base64'
      )
      response.headers['cache-control'] = [{ key: 'cache-control', value: 'max-age=31536000' }]
    } catch (err) {
      console.error(err)
    }
  } else {
    headers['content-type'] = WEBP_CONTENT_HEADER
  }

  return response
}

标签: aws-lambdaamazon-cloudfrontaws-lambda-edge

解决方案


推荐阅读