Я хочу отправить сжатые данные (gzip) на некоторый URL-адрес, который вызовет лямбда-функцию (прокси), которая распаковывает данные.
Лямбда-функция (NodeJS 8):
let zlib = require('zlib');
exports.handler = async (event) => {
let decompressedData = zlib.gunzipSync(event['body'])
return {
"statusCode": 200,
"body": decompressedData.toString()
};
};
Я запускаю его с помощью команды curl для URL-адреса (через API-шлюз) для некоторого файла, который я сжал example.gz
с помощью gzip:
curl -X POST --data-binary @example.gz https://URL...
В результате получаю:
{"message": "Internal server error"}
И ошибка (логи в Cloudwatch):
"errorMessage": "incorrect header check",
"errorType": "Error",
"stackTrace": [
"Gunzip.zlibOnError (zlib.js:153:15)",
"Gunzip._processChunk (zlib.js:411:30)",
"zlibBufferSync (zlib.js:144:38)",
"Object.gunzipSync (zlib.js:590:14)",
"exports.handler (/var/task/test_index.js:5:33)"
]
Когда я смотрю на сам event['body']
, я вижу те же данные, что и в example.gz
. Возможно, мне нужен какой-то специальный заголовок? Я просто хочу передать данные как есть.
По умолчанию API Gateway не может передавать двоичные данные в функцию Lambda event['body']
без их повреждения, поскольку это двоичные данные, а event['body']
— это строка, передаваемая Lambda в формате JSON. Его нужно будет завернуть в base-64. Ваша строка может выглядеть так же, но она почти наверняка не будет идентична байт за байтом после того, как попадет в лямбда-функцию. docs.aws.amazon.com/apigateway/latest/developerguide/…
@Michael-sqlbot Спасибо! Пожалуйста, добавьте свой комментарий в качестве ответа, чтобы я мог хотя бы вознаградить вас за то, что вы направили меня к решению. Во всяком случае, я добавил свой ответ.
@sheldonzy спасибо за предложение, но ваш ответ очень хорош. Я указал вам правильное направление, но вы побежали с ним и решили его, так что +1 от меня. Я не обижусь, если вы отметите свой ответ как принятый ответ.
1/ Сначала вам нужно правильно собрать gzip, убедитесь, что заголовок файла gzip отсутствует: Команда curl отправляет сжатое тело POST на сервер apache.
Неправильный способ :
echo '{ "mydummy" : "json" }' > body
gzip body
hexdump -C body.gz
00000000 1f 8b 08 08 20 08 30 59 00 03 62 6f 64 79 00 ab |.... .0Y..body..|
00000010 56 50 ca ad 4c 29 cd cd ad 54 52 b0 52 50 ca 2a |VP..L)...TR.RP.*|
00000020 ce cf 53 52 a8 e5 02 00 a6 6a 24 99 17 00 00 00 |..SR.....j$.....|
00000030
Хороший способ :
echo '{ "mydummy" : "json" }' | gzip > body.gz
hexdump -C body.gz
00000000 1f 8b 08 00 08 0a 30 59 00 03 ab 56 50 ca ad 4c |......0Y...VP..L|
00000010 29 cd cd ad 54 52 b0 52 50 ca 2a ce cf 53 52 a8 |)...TR.RP.*..SR.|
00000020 e5 02 00 a6 6a 24 99 17 00 00 00 |....j$.....|
0000002b
2/ В curl не забудьте указать кодировку содержимого с помощью
-H "Content-Encoding: gzip"
3/Кроме того, если вы используете экспресс+сжатие, вам не нужно вызывать zlib
curl -X POST "http://example.org/api/a" -H "Content-Encoding: gzip" -H "Content-Type: application/json" --data-binary @body.gz
router.post("/api/a", function(req, res){
console.info(req.body); // { mydummy: 'json' }
});
На самом деле я сейчас делаю это с Content-Encoding: gzip
, но это так медленно, что я хочу попробовать распаковать в самой лямбде.
как сказал Майкл - sqlbot, по умолчанию шлюз API не может передавать двоичные данные в функцию Lambda.
Что сработало для меня:
Я добавил заголовок Content-Type: application/octet-stream
в команду curl, а в настройках API-шлюза на Binary Media Types
добавил application/octet-stream
.
Таким образом, данные передаются в формате base64, а затем я просто преобразовал дату в формате base64 в буфер:
let data = Buffer.from(event['body'], "base64")
А потом просто распаковать.
Для получения дополнительной информации читать здесь
Пожалуйста, разместите тело события