Search code examples
node.jsamazon-web-servicesamazon-s3aws-lambdafile-type

The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined


I'm trying to upload a file (pdf/jpg) using a Lambda function written in NodeJS by triggering the request from Postman but I'm getting the following error:-

2022-02-02T15:09:51.135Z    743939db-7511-4003-8e49-40c95ada47b4    ERROR   Invoke Error    
{
    "errorType": "TypeError",
    "errorMessage": "The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined",
    "code": "ERR_INVALID_ARG_TYPE",
    "stack": [
        "TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined",
        "    at new NodeError (internal/errors.js:322:7)",
        "    at Function.from (buffer.js:334:9)",
        "    at Runtime.exports.lambdaHandler [as handler] (/var/task/app.js:68:23)"
    ]
}

The following is a chunk of event object getting logged on the CloudWatch:-

2022-02-02T20:39:52.136+05:30

Copy
info: Event:: {"body":"{\n    \"base64String\": \"/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAcHBwcIBwgJCQgMDAsMDBEQDg4QERoSFBIUEhonGB0YGB0YJyMqIiAiKiM+MSsrMT5IPDk8SFdOTldtaG2Pj8ABBwcHBwgHCAkJCAwMCwwMERAODhARGhIUEhQSGicYHRgYHRgnIyoiICIqIz4xKysxPkg8OTxIV05OV21obY+PwP/CABEICHAPAAMBIgACEQEDEQH/xAAcAAEAAgMBAQEAAAAAAAAAAAAABwgBBQYEAwL/2gAIAQEAAAAAsiAA

Lambda (NodeJS code):-

'use-strict'

const AWS = require("aws-sdk");

const logger = require('./logger').logger;

const moment = require('moment');

const fileType = ('file-type');

const { Buffer } = require('buffer');

//const { fileTypeFromFile } = 'file-type';

const ddbTable = process.env.RUNTIME_DDB_TABLE_FREE_USER_DOCUMENT;

const s3TempBucket = process.env.RUNTIME_S3_TEMP_BUCKET;

const s3 = new AWS.S3();

const getFile = (fileMime, buffer, userId) => {
  let fileExt = fileMime.ext;
  let hash = sha1(new Buffer(new Date().toString()));
  let now = moment().format('YYYY-MM-DD HH:mm:ss');

  let filePath = hash + '/';
  let fileName = unixTime(now) + '.' + fileExt;
  let fileFullName = filePath + fileName;
  let fileFullPath = s3TempBucket + userId + fileFullName;

  const params = {
    Body: buffer,
    Bucket: s3TempBucket,
    Key: fileName
  };

  let uploadFile = {
    size: buffer.toString('ascii').length,
    type: fileMime.mime,
    name: fileName,
    fullPath: fileFullPath
  }

  return {
    'params': params,
    'uploadFile': uploadFile
  }
}

exports.lambdaHandler = async (event, context) => {
  logger.info("Event::", event);
  logger.info('Uploading file to bucket::', s3TempBucket);

  let body, data;
  let statusCode = 200;
  const headers = {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Headers': 'Content-Type',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': '*'
  };

  let request = JSON.parse(event.body);
  let base64String = await request.base64String;
  logger.info("base64String::", base64String);

  let buffer = Buffer.from(base64String, 'base64');

  //let buffer = new Buffer(base64String, 'base64');
  let fileMime = fileType(buffer);
  logger.info(fileMime);

  if (fileMime === null) {
    return context.fail('String supplied is not file type');
  }

  //let file = getFile(fileMime, buffer, user.id);
  let file = getFile(fileMime, buffer, 'b06eb6f4-0ff0-5cb5-a41c-e000af66c8e9');
  let params = file.params;

  try {
    //await new Promise((resolve, reject) => {
      s3.putObject(params, (err, results) => {
        if (err) reject(err);
        else {
          console.log(results);
          body = results;
          resolve(results)
        }
      });
   // });

  } catch (err) {
    logger.info(err);
    statusCode = 400;
    body = err.message;
    return err;
  } finally {
    body = JSON.stringify(data);
  }

  return {
    statusCode,
    body,
    headers
  };

}

The base64String is coming as undefined not sure why as I can see clearly in the event object?:-

let buffer = Buffer.from(base64String, 'base64');

Please assist, thanks

Postman request:-

enter image description here


Solution

  • If you use API Gateway, you don't need base64 encode because API Gateway does automatically.

    A sample is provided in AWS.

    1. Select Create function
    2. Select Browse serverless app repository
    3. Find "uploader: Serverless web application for uploading files to S3"
    4. Deploy

    uploader's github

    This creates API gateway and NodeJS Lambda. (You need to provide S3 bucket.)

    The instruction says that to upload a file, open InvokeURL in browser and drag drop a file. You can do this with Postman too as follows.

    1. Input POST InvokeURL/api/file/text.pdf.
    2. Set body KEY to File and input text.pdf. Select the pdf file as VALUE.
    3. Send

    You can find the code in index.js and extract what you need.