Как вернуть двоичные данные из лямбда-функции в AWS в Python?

Я не могу заставить Python Lambda возвращать двоичные данные. Шаблон узла для миниатюрных изображений работает нормально, но я не могу заставить работать Python-лямбду. Ниже приведены соответствующие строки из моей лямбды. print("image_data " + image_64_encode) печатает изображение в кодировке base64 в журналах.

def lambda_handler(event, context):
    img_base64 = event.get('base64Image')
    if img_base64 is None:
        return respond(True, "No base64Image key")

    img = base64.decodestring(img_base64)
    name = uuid.uuid4()
    path = '/tmp/{}.png'.format(name)

    print("path " + path)

    image_result = open(path, 'wb')
    image_result.write(img)
    image_result.close()

    process_image(path)

    image_processed_path = '/tmp/{}-processed.png'.format(name)
    print("image_processed_path " + image_processed_path)
    image_processed = open(image_processed_path, 'rb')
    image_processed_data = image_processed.read()
    image_processed.close()
    image_64_encode = base64.encodestring(image_processed_data)

    print("image_data " + image_64_encode)


    return respond(False, image_64_encode)


def respond(err, res):
    return {
        'statusCode': '400' if err else '200',
        'body': res,
        'headers': {
            'Content-Type': 'image/png',
        },
        'isBase64Encoded': 'true'
    }

Любые указатели на то, что я делаю не так?

Ответ 1

Выполнение всех описанных выше шагов не сработало в моем случае, потому что двоичная поддержка content-type = */* преобразует все ответы в двоичные.

Мой случай:

  • Несколько лямбда-функций, возвращающих json (текст), только одна лямбда-функция, возвращающая двоичный файл. У всех есть лямбда-прокси.

  • Лямбды находятся в шлюзе API

  • API-шлюз находится за CloudFront

Подсказка: Я заметил важную информацию в API Gateway → Настройки

Binary support description

Цитирование:

API Gateway рассмотрит Content-Type и Accept HTTP-заголовки, чтобы решить, как обрабатывать тело.

Это означает, что заголовок ответа Content-Type должен соответствовать заголовку запроса request header

Решение:

  1. Установите двоичные типы носителей в API-шлюзе на тип mime: image/jpg

  2. В вашем HTTP-запросе установите Accept: image/jpg

  3. В вашем HTTP-ответе установлено Content-Type: image/jpg

{
  "isBase64Encoded": True,
  "statusCode": 200,
  "headers": { "content-type": "image/jpg"},
  "body":  base64.b64encode(content_bytes).decode("utf-8")
}
  1. Затем мы должны указать CloudFront принять заголовок "Accept" из запроса. Итак, в дистрибутиве CloudFront нажмите на свой экземпляр API Gateway (ID кликабелен) и после перенаправления на экземпляр CloudFront перейдите на вкладку Поведение, выберите шаблон пути вашего API (пример: /api/*) и нажмите кнопку Изменить.

Example of path patterns

На новом экране необходимо добавить заголовок Accept в белый список.

whitelist Accept

Примечание 1. Если у вас несколько типов файлов, вы должны добавить их все в двоичные типы носителей в настройках шлюза API

Примечание 2: Если вы пришли из без сервера и хотите установить типы двоичных файлов при развертывании лямбда файлов, проверьте этот пост: настройка двоичных типов мультимедиа для шлюза API

plugins:
  - serverless-apigw-binary

custom:
  apigwBinary:
    types:
- 'image/jpeg'

Файл serverless.yml для облачного фронта должен содержать:

resources:
    WebAppCloudFrontDistribution:
      Type: AWS::CloudFront::Distribution
      Properties:
        DistributionConfig:
          ...
          CacheBehaviors:
            ...
            - 
              #API calls
              ...
              ForwardedValues:
                ...
                Headers:
                  - Authorization
                  - Accept

Ответ 2

Я, наконец, понял это. Возвращаемые двоичные данные из lambda python выполняются.

Следуйте инструкциям здесь: https://aws.amazon.com/blogs/compute/binary-support-for-api-integrations-with-amazon-api-gateway/

Обязательно проверьте "Использовать интеграцию прокси-сервера Lambda" при создании нового метода.

Также убедитесь, что ваш ответ на лямбду python выглядит так:

return {'isBase64Encoded'   : True,
        'statusCode'        : 200,
        'headers'           : { 'Content-Type': content_type },
        'body'              : base64_encoded_binary_data}

ЗАТЕМ:

Для каждого из ваших маршрутов/методов:

apigateway update-integration-response --rest-api-id <api-id> --resource-id <res-id> --http-method POST --status-code 200 --patch-operations "[{\"op\" : \"replace\", \"path\" : \"/contentHandling\", \"value\" : \"CONVERT_TO_BINARY\"}]"

В консоли AWS. Их можно увидеть в "панировочных судах" API Gateway:

<api-id> = zdb7jsoey8
<res-id> = zy2b5g

THEN: вам нужно "Развернуть API". Из того, что я нашел, он работал только после развертывания API.

Перед развертыванием убедитесь, что вы установили "Бинарные типы носителей".

Подсказка: Nice терминал AWS оболочки здесь: https://github.com/awslabs/aws-shell

pip install aws-shell

Ответ 3

Насколько я могу судить, это также относится и к Python 3. Я пытаюсь вернуть двоичные данные (байты). Это не работает вообще.

Я также пытался использовать кодировку base-64, но у меня ничего не получилось.

Это с помощью API Gateway и Proxy Integration.

[Обновить]

Я наконец понял, как это сделать. Я включил двоичную поддержку для типа */* а затем вернул это:

return({
        "isBase64Encoded": True,
        "statusCode": 200,
        "headers": {
                "content-type": "image/jpg",
        },  
        'body':  base64.b64encode(open('image.jpg', 'rb').read()).decode('utf-8')
})  

Ответ 4

Я столкнулся с той же проблемой около 6 месяцев назад. Похоже, хотя в API Gateway теперь есть двоичная поддержка (и примеры в JS), Python 2.7 Lambda все еще не поддерживает корректный двоичный ответ, не уверен в Python 3.6.

У кодированного ответа Base64 возникают проблемы из-за упаковки JSON. Я написал пользовательскую JS на стороне клиента, снимая изображение базы 64 из этого JSON вручную, но это было также плохое решение.

Загрузите результат на S3 (за CloudFront) и верните 301 в CloudFront, кажется, хорошим решением. Работает лучше всего для меня.