【问题标题】:How do you get the HEAD object from an s3 in node.js? (running in an aws lambda)如何从 node.js 中的 s3 获取 HEAD 对象? (在 aws lambda 中运行)
【发布时间】:2019-07-24 01:27:14
【问题描述】:

我正在尝试运行一个 lambda,每次将一个对象放入我的 S3 存储桶时,它都会将元数据插入我的数据库。

因为我使用的是 MongoDB,所以我有代码可以在调用之间保持数据库连接。我遇到的问题是我无法获取元数据。

所有插入数据库的代码都已完成,我只需要帮助从 aws lambda 获取元数据。

这是我的代码(主要是从 MongoDB site 复制而来)

"use strict";
const MongoClient = require('mongodb').MongoClient;
const MONGODB_URI = 'mongodb://cam_writer:1%40kGM%26LL%26gA5y7NVk1cvl9@cluster0-shard-00-00-hlygq.mongodb.net:27017,cluster0-shard-00-01-hlygq.mongodb.net:27017,cluster0-shard-00-02-hlygq.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin&retryWrites=true'; // or Atlas connection string

var AWS = require('aws-sdk')
var s3 = AWS.S3()
let cachedDb = null;

function connectToDatabase (uri) {

  console.log('=> connect to database');

  if (cachedDb) {
    console.log('=> using cached database instance');
    return Promise.resolve(cachedDb);
  }



  return MongoClient.connect(uri)
    .then(client => {
      cachedDb = client.db('events');
      return cachedDb;
    });

}


function queryDatabase (db) {
  console.log('=> query database');

  return db.collection('detection_events').find({}).toArray()
    .then(() => { return { statusCode: 200, body: 'success' }; })
    .catch(err => {
      console.log('=> an error occurred: ', err);
      return { statusCode: 500, body: 'error' };
    });
}

function insertIntoDb (db, obj) {
  console.log('=> inserting data into db');

  return db.collection('detection_events').insertOne(obj)
}

module.exports.handler = (event, context, callback) => {

  context.callbackWaitsForEmptyEventLoop = false;

  console.log(event)

  var meta = {test : "test", "key": event}; // HOW DO I GET THE ACTUAL METADATA FOR THE EVENT?

  console.log('event: ', event);

  connectToDatabase(MONGODB_URI)
    .then(db => insertIntoDb(db, meta))
    .then(result => {
      console.log('=> returning result: ', result);
      callback(null, result);
    })
    .catch(err => {
      console.log('=> an error occurred: ', err);
      callback(err);
    });
};

我知道 s3 传递给 lambda 的“事件”不包含元数据。在 python 中,我能够通过使用 boto3 来获取元数据,我只是不知道如何在 node.js 中执行它,(更不用说 aws lambda 中的 node.js)

编辑:

所以我根据下面的第一个答案更新了我的代码。现在的代码是:

"use strict";
const MongoClient = require('mongodb').MongoClient;
const MONGODB_URI = 'mongodb://cam_writer:1%40kGM%26LL%26gA5y7NVk1cvl9@cluster0-shard-00-00-hlygq.mongodb.net:27017,cluster0-shard-00-01-hlygq.mongodb.net:27017,cluster0-shard-00-02-hlygq.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin&retryWrites=true'; // or Atlas connection string

const AWS = require('aws-sdk')
const s3 = new AWS.S3()
let cachedDb = null;

const connectToDatabase = uri => {

    console.log('=> connect to database');

    if (cachedDb) {
        console.log('=> using cached database instance');
        return Promise.resolve(cachedDb);
    }

    return MongoClient.connect(uri)
        .then(client => {
            cachedDb = client.db('events');
            return Promise.resolve(cachedDb);
        });

}

function insertIntoDb(db, obj) {
    console.log('=> inserting data into db');

    return db.collection('detection_events').insertOne(obj)
}

module.exports.handler = async (event) => {

    const db = await connectToDatabase(MONGODB_URI);

    //finally get the HEAD for the s3Object
    const head = await s3.headObject({
        Bucket: event.Records[0].s3.bucket.name,
        Key: event.Records[0].s3.object.key
    }).promise();

    var meta = head['Metadata']
    meta['bucket'] = event.Records[0].s3.bucket.name,
    meta['key'] = event.Records[0].s3.object.key
    console.log(meta)

    const result = await insertIntoDb(db, meta)

    console.log(result)
    return {
        statusCode: 201,
        body: JSON.stringify(result)
    }
};

我运行了我的代码,它将一堆图像插入到 s3 中。这在 mongodb 中生成了大约 25 个连接,我怎样才能使 lambda 的连接保持在低水平?我认为从 mongoDB 网站复制的代码可以让我这样做

【问题讨论】:

    标签: node.js amazon-s3 aws-lambda


    【解决方案1】:

    由于您使用的是 S3 Event,您可以通过访问 event.Records[0].s3.bucket.nameevent.Records[0].s3.object.key 获取 s3 bucketkey。使用以下代码可以轻松完成:

    const params = {
      Bucket: event.Records[0].s3.bucket.name, 
      Key: event.Records[0].s3.object.key
     };
     s3.headObject(params, function(err, data) {
      if (err) {
        console.log(err, err.stack);
        return;
      }
      console.log(data)
    });
    

    只要确保你把它放在你的数据库回调中,否则,你会忘记它。

    我强烈建议您使用async/await,因为您不必处理著名的callback hell。这是重构后的代码:

    "use strict";
    const MongoClient = require('mongodb').MongoClient;
    const MONGODB_URI = 'mongodb://cam_writer:1%40kGM%26LL%26gA5y7NVk1cvl9@cluster0-shard-00-00-hlygq.mongodb.net:27017,cluster0-shard-00-01-hlygq.mongodb.net:27017,cluster0-shard-00-02-hlygq.mongodb.net:27017/test?ssl=true&replicaSet=Cluster0-shard-0&authSource=admin&retryWrites=true'; // or Atlas connection string
    
    const AWS = require('aws-sdk')
    const s3 = AWS.S3()
    let cachedDb = null;
    
    const connectToDatabase = uri => {
    
      console.log('=> connect to database');
    
      if (cachedDb) {
        console.log('=> using cached database instance');
        return Promise.resolve(cachedDb);
      }
    
      return MongoClient.connect(uri)
        .then(client => {
          cachedDb = client.db('events');
          return Promise.resolve(cachedDb);
        });
    
    }
    
    function insertIntoDb (db, obj) {
      console.log('=> inserting data into db');
    
      return db.collection('detection_events').insertOne(obj)
    }
    
    module.exports.handler = async (event) => {
    
      const db = await connectToDatabase(MONGODB_URI);
    
      const result = await insertIntoDb(db, {
        bucket: event.Records[0].s3.bucket.name,
        key: event.Records[0].s3.object.key
      })
    
      console.log(result)
    
      //finally get the HEAD for the s3Object
       const head = await s3.headObject({
         Bucket: event.Records[0].s3.bucket.name,
         Key: event.Records[0].s3.object.key
       }).promise();
    
       console.log(head)
    
      return {
        statusCode: 201,
        body: JSON.stringify(result)
      }
    
    };
    

    这应该足以让你离开地面。

    编辑:我建议您查看official NodeJS SDK,因为它的文档非常好

    EDIT 2:根据Michael's 的建议,如果您的文件可能包含空格,请使用decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " ")) 而不是event.Records[0].s3.object.key

    编辑 3:现在您的代码工作正常,您说它向 S3 添加了“一堆”图像。 S3 将触发与插入图像一样多的事件。这意味着 N 个 Lambda 将同时启动,因此每次都会创建一个新的 MongoDB 连接。

    这里的一种解决方法是将 Lambda 函数上的并发执行限制设置为较低的数字,这样您就可以控制可以同时打开多少连接。

    为了做到这一点,请转到您的 Lambda 控制台并将并发设置为您想要的任何数字(在下面的示例中,我使用 5)。这应该足以满足您的需求。

    【讨论】:

    • 在 S3 事件中,对象键以 S3 的半标准编码格式呈现。 + 字符必须替换为 ASCII 0x20(空格),并且结果值必须经过 URL 解码。如果不这样做,SDK 将进行双重编码,并且 HEAD 将失败如果对象键包含空格或 S3 应用这些内部规则的任何 unicode 字符。正确提取密钥的表达式:decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "))docs.aws.amazon.com/lambda/latest/dg/…
    • 迈克尔,你一如既往地正确。不过,出于简单的原因,我将其省略了,但我不应该那么懒惰:)。有趣的是,我上周举办了一个无服务器研讨会,但我没有处理空格,所​​以当然,有人上传了一个名称上有空格的文件并遇到了问题。我设法修复了上面的正则表达式,但这是一个教训。无论如何,我认为应用程序应该在上传时处理空格,因此避免这些类型的错误,毕竟,文件名上的空格被认为是一种不好的做法。
    • 薛定谔的猫和海森堡? :)
    • @A_toaster 视情况而定。如果您正在触发并发连接,则会启动一个新的 Lambda 容器,从而在您的 MongoDB 上再创建一个连接。如果它使用相同的容器,它只会重用相同的连接。
    • 请检查这个答案,因为我大致解释了缓存/容器的工作原理,以便您更好地了解可能发生的事情:stackoverflow.com/questions/54947095/aws-lambda-mysql-caching/…
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-19
    • 2018-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多