2

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

John Rotenstein
  • 241,921
  • 22
  • 380
  • 470
vinod827
  • 1,123
  • 1
  • 21
  • 45
  • This might help. https://stackoverflow.com/questions/13807339/upload-a-binary-file-to-s3-using-aws-sdk-for-node-js – shimo Feb 02 '22 at 21:19
  • @shimo I have gone through with that link and tried attempting it but that solution will not work as it requires middleware like express but here I'm looking for a pure Lambda-based solution... I had researched a lot but did not find a single solution on the web for the last 3 weeks that gives a NodeJS based Lambda to upload a file...and I'm really frustrating to be honest – vinod827 Feb 04 '22 at 16:27
  • Sorry about that. I've post an answer that use sample of Lambda repository. Anyway it works. – shimo Feb 06 '22 at 12:48

1 Answers1

2

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.

shimo
  • 2,156
  • 4
  • 17
  • 21