s3PutObject has stopped working all of a sudden from Lambda - node.js

I have this code in my Lambda function
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
exports.handler = (event, context, callback) => {
const bucketName = "dhaval-upload";
let data = {
firstname: event.firstname,
lastname: event.lastname,
email: event.email,
userAgent: event.userBrowser,
userIP: event.userIP
};
let params = {
Body: JSON.stringify(data),
Bucket: bucketName,
Key: event.email
};
s3.putObject(params);
callback(null, { message: event.email});
};
This returns the success callback but does not write the object in s3.
and this was working but now for some reason it does not.
Please Note :- I checked cloudwatch logs and could not find the reason for the issue from it.

may be you can try it like this, you should use callbacks / async to your code
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
exports.handler = (event, context, callback) => {
const bucketName = "dhaval-upload";
let data = {
firstname: event.firstname,
lastname: event.lastname,
email: event.email,
userAgent: event.userBrowser,
userIP: event.userIP
};
let params = {
Body: JSON.stringify(data),
Bucket: bucketName,
Key: event.email
};
// s3.putObject(params);
// callback(null, { message: event.email});
// === it works like this ===
s3.putObject(params, (err, data) => {
if (err) callback(null, err);
else callback(null, { message: event.email });
});
};

Related

Node.js upload Image Stream.Readable to S3

My lambda is triggered by a request from the browser. The browser sends an image as multipart/form-data.
The lambda uses busboy to parse the request:
function parseForm(event: IHttpEvent) {
return new Promise(
(resolve, reject) => {
const busboy = new Busboy({
headers: event.headers,
limits: { files: 10 },
});
const imageResponse = new Map<string, IImageParseResponse>();
busboy.on("file", (id, file, filename, encoding, mimeType) => {
imageResponse.set(id, { file, filename, mimeType });
});
busboy.on("error", (error) => reject(`Parse error: ${error}`));
busboy.on("finish", () => resolve(imageResponse));
busboy.write(event.body, event.isBase64Encoded ? "base64" : "binary");
busboy.end();
}
);
}
When I parsed the request I want to upload the file to AWS S3.
export async function handler(event: IHttpEvent) {
var res = await parseForm(event);
const s3 = new S3Client({ region: "eu-central-1" });
for (const [k, v] of res) {
console.log(`File ${v.filename} ${v.mimeType} streaming`);
const stream = new Readable().wrap(v.file);
const upload = new Upload({
client: s3,
params: {
Key: v.filename,
Bucket: "my-image-bucket",
Body: stream,
ContentType: v.mimeType,
},
});
upload.on("httpUploadProgress", (p) => console.log(p));
const result = await upload.done();
console.log(result);
return result;
}
}
This does not work. However the Browser will receive a 200 OK with a null body response. What confuses me even more is that console.log(result); does not log anything to console.
Where is my mistake? I dont't fully understand the mechanics of streams. But as far as I understand it will be more memory-efficient. In the future I plan to upload multiple images at once. And in order to save cost I want my method to be as efficient as possible.
In general I did 2 mistakes.
Tried to upload the stream when it was already read to the end by busboy
I did not properly wait for the completion of the upload to s3 before terminating the function.
In the end i ended up with the following:
const s3 = new S3Client({ region: "eu-central-1" });
const { BUCKET_NAME, MAX_IMAGE_SIZE } = process.env;
export async function handler(event: IHttpEvent) {
const results = await parseForm(event);
const response = [];
for (const r of results) {
if (r.status === "fulfilled") {
const value: any = r.value.result;
response.push({
id: r.value.id,
key: value.Key,
url: value.Location,
});
}
if (r.status === "rejected")
response.push({ id: r.reason.id, reason: r.reason.error });
}
return response;
}
async function doneHandler(
id: string,
uploadMap: Map<string, Upload>
): Promise<{ id: string; result: ServiceOutputTypes }> {
try {
var result = await uploadMap.get(id).done();
} catch (e: any) {
var error = e;
} finally {
uploadMap.delete(id);
if (error) throw { id, error };
return { id, result };
}
}
function parseForm(event: IHttpEvent) {
return new Promise( (resolve, reject) => {
const busboy = new Busboy({
headers: event.headers,
limits: { files: 1, fileSize: parseInt(MAX_IMAGE_SIZE) },
});
const responses: Promise<{
id: string;
result: ServiceOutputTypes;
}>[] = [];
const uploads = new Map<string, Upload>();
busboy.on("file", (id, file, filename, encoding, mimeType) => {
uploads.set(
id,
new Upload({
client: s3,
params: {
Bucket: BUCKET_NAME,
Body: new Readable().wrap(file),
Key: filename,
ContentType: mimeType,
ContentEncoding: encoding,
},
})
);
responses.push(doneHandler(id, uploads));
file.on("limit", async () => {
const aborts = [];
for (const [k, upload] of uploads) {
aborts.push(upload.abort());
}
await Promise.all(aborts);
return reject(new Error("File is too big."));
});
});
busboy.on("error", (error: any) => {
reject(new Error(`Parse error: ${error}`));
});
busboy.on("finish", async () => {
const res = await Promise.allSettled(responses);
resolve(res);
});
busboy.write(event.body, event.isBase64Encoded ? "base64" : "binary");
busboy.end();
}
);
}
This solution also handles file-limits and tries to abort all pending uploads to S3

S3 callbacks get ignored

I'm trying to upload a base64 encoded image to S3 through this route, but the callbacks get completely ignored and the code jumps straight to res.json("SUCCESS");
route
AWS.config.update({
accessKeyId: "xxxxxxxxxxxxxx",
secetAccessKey: "xxxxxxxxxxxxxxxxxxxxxx",
region: "us-east-1"
});
const s3 = new AWS.S3();
....
router.post("/imageupload", async (req, res) => {
const base64 = req.body.base64;
try {
const params = {
Bucket: process.env.bucketName,
Key: "images/newImage",
Body: base64
};
await s3.putObject(params, function(err, data) {
if (err) res.json(err);
else res.json(data);
});
res.json("SUCCESS");
} catch (e) {
console.log(e.message);
res.status(500).json(e.message);
}
});
Any help is much appreciated thanks!
EDIT FIXED:
I figured out what the problem was:
I had recently reformatted my computer which meant I had to reinstall AWS cli AND reconfigure aws creds.
That was it.
The AWS documentation for using-promises.
var s3 = new AWS.S3({apiVersion: '2006-03-01', region: 'us-west-2'});
var params = {
Bucket: 'bucket',
Key: 'example2.txt',
Body: 'Uploaded text using the promise-based method!'
};
var putObjectPromise = s3.putObject(params).promise();
putObjectPromise.then(function(data) {
console.log('Success');
}).catch(function(err) {
console.log(err);
});
You can also promisify all functions by using a library such as bluebird
AWS.config.setPromisesDependency(require('bluebird'));
Here's an example using your code
router.post("/imageupload", async (req, res) => {
const base64 = req.body.base64;
try {
const params = {
Bucket: process.env.bucketName,
Key: "images/newImage",
Body: base64
};
const data = await s3.putObject(params).promise()
res.json(data);
} catch (e) {
console.log(e.message);
res.status(500).json(e.message);
}
});

errorMessage: "event is not defined" in lambda function nodejs

I am trying to run a lambda function attached to an API gateway GET request and below is the code
const AWS = require('aws-sdk');
const s3 = new AWS.S3();
const bucketName = "dhaval-upload";
let params = {
Bucket: bucketName,
Key: event.fileName
};
exports.handler = async (event, context, callback) => {
return await s3.getObject(params).promise()
.then((res) => {
return "abcd";
// return res.Body.toString('utf-8');
})
.catch((err) => {
return err;
});
};
but I am getting the below error
errorMessage: "event is not defined"
errorType: "ReferenceError"
But I don't understand the reason for this as I have another POST request running perfectly..
Any help will be highly appreciated
You need to place params inside your handler, like this:
exports.handler = async (event, context, callback) => {
let params = {
Bucket: bucketName,
Key: event.fileName
};
return await s3.getObject(params).promise()
.then((res) => {
return "abcd";
// return res.Body.toString('utf-8');
})
.catch((err) => {
return err;
});
};

Issue with invoking lambda from another lambda

I have 2 Lambda functions that I would like to call directly not through API Gateway. Lambda function A is Calling Lambda B along with queryStringParameters. For some reasons I'm getting this error
{ UnexpectedParameter: Unexpected key 'queryStringParameters' found in params
This is my Lambda A function
var aws = require('aws-sdk');
var lambda = new aws.Lambda({
region: 'eu-central-1'
});
exports.handler = (event, context, callback) => {
var params = {
FunctionName: "function-getStats",
InvocationType: "RequestResponse",
LogType: "Tail",
"queryStringParameters" : { "fn" : "latest_no" }
};
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
};
Which is calling Lambda B as below
var AWS = require('aws-sdk');
AWS.config.update({region: 'eu-central-1'});
var ddb = new AWS.DynamoDB.DocumentClient({apiVersion: '2012-08-10'});
exports.handler = (event, context, callback) => {
var fn = event["queryStringParameters"]['fn'];
...
..
//If successful return the following response
console.log("Success", items);
callback(null, {
'statusCode': '200',
'body': JSON.stringify(items),
'headers': {
"Access-Control-Allow-Origin": "*"
},
'isBase64Encoded': false
});
Can someone please advise how to fix this?
In case anyone got the same issue. here's the approach I did
var aws = require('aws-sdk');
var lambda = new aws.Lambda({
region: 'eu-central-1'
});
exports.handler = (event, context, callback) => {
event.queryStringParameters= {"fn" : "latest_no" };
var params = {
FunctionName: "function-getStats",
InvocationType: "RequestResponse",
LogType: "Tail",
Payload: JSON.stringify(event, null, 2),
};
lambda.invoke(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
};

Upload a file from S3 to Youtube using node.js

Uploading a file from S3 to youtube, using this code:
s3.setBucket('MyBucket');
s3.get('MyBucket/5/' + filename, null, 'stream', function(err, response) {
googleapis.discover('youtube', 'v3').execute(function(err, client) {
var metadata = {
snippet: { title: title, description: description},
status: { privacyStatus: 'public' }
};
client
.youtube.videos.insert({ part: 'snippet,status'}, metadata)
.withMedia('video/mp4', response)
.withAuthClient(auth)
.execute(function(err, result) {
if (err) console.log(err);
else console.log(JSON.stringify(result, null, ' '));
response.redirect('/share?set=yt&id=' + result.id);
}); }); });
does not work, because the line:
.withMedia('video/mp4', response)
It's a replacement of the original one, that works:
fs.readFileSync('/temp/myfile.png')
In other words: If I upload a local file on my laptop, this will work because I'm using the filesystem object.
In case anyone is looking for the answer, here it is:
var googleapis = require('googleapis'),
OAuth2 = googleapis.auth.OAuth2,
ytdapi = googleapis.youtube('v3'),
AWS = require('aws-sdk'),
s3 = new AWS.S3;
var s3data = {
Bucket: 'BUCKET_NAME',
Key: 'VIDEO_NAME'
};
s3.getObject(s3data, function (err, data) {
var params = {
auth: oauth2Client,
part: 'snippet,status',
resource: {
snippet: {
title: 'Title',
description: 'Description'
},
status: {
privacyStatus: 'public'
}
},
media: {
mimeType: 'video/mp4',
body: data.Body
}
};
ytdapi.videos.insert(params, function (err, result) {
if (err) console.log(err);
else console.log(JSON.stringify(result, null, ' '));
});
});
This question is a bit old but here is how I uploaded a video stored in S3 to Youtube using NodeJS v6.10.3.
const AWS = require('aws-sdk'); // v1.6.0
const s3 = new AWS.S3({region: 'eu-west-1', apiVersion: '2006-03-01'});
const {google} = require('googleapis'); // v26.0.1
const YoutubeApi = google.youtube('v3');
const s3data = {
Bucket: bucketName,
Key: fileName
};
let fileStream = s3.getObject(s3data).createReadStream();
const params = {
auth: oauth2Client, // create this using 'google-auth-library'
part: 'snippet,status',
resource: {
snippet: {
title: 'Title',
description: 'description'
},
status: {
privacyStatus: 'private'
}
},
media: {
mimeType: 'video/mp4',
body: fileStream // stream to stream copy
}
};
YoutubeApi.videos.insert(params, function (err, result) {
if (err) {
console.error('error');
console.error(err);
}
else {
console.log('success');
}
});

Resources