Set Thumbnail image Content-Type - node.js

I need to set Content-Type for the thumbnail image. I have tried as shown below.But it is not working.Still, it stores as a stream.
Azure function:
index.json
var Jimp = require("jimp");
module.exports = (context, myBlob) => {
// Read image with Jimp
Jimp.read(myBlob).then((image) => {
// Manipulate image
image
.resize(200, Jimp.AUTO)
.greyscale()
.getBuffer(Jimp.MIME_JPEG, (error, stream) => {
if (error) {
context.log(`There was an error processing the image.`);
context.done(error);
}
else {
context.log(`Successfully processed the image`);
stream.set("Content-Type", Jimp.MIME_JPEG); // here need to set the Content-Type
context.done(null, stream);
}
});
});
};
function.json
{
"bindings": [
{
"name": "myBlob",
"type": "blobTrigger",
"direction": "in",
"path": "project2-photos-original/{name}",
"connection": "thumbnailfunction_STORAGE",
"dataType": "binary"
},
{
"type": "blob",
"name": "$return",
"path": "project2-photos-thumbnail/{name}",
"connection": "thumbnailfunction_STORAGE",
"direction": "out"
}
],
"disabled": false
}
I have seen the same implementation like this on NodeJs
var Jimp = require("jimp");
var express = require("express");
var app = express();
app.get("/my-dynamic-image", function(req, res){
Jimp.read("lenna.png", function(err, lenna) {
lenna.resize(64, 64).quality(60).getBuffer(Jimp.MIME_JPEG, function(err, buffer){
res.set("Content-Type", Jimp.MIME_JPEG);
res.send(buffer);
});
});
});
app.listen(3000);
Question: Can you tell me how to set Content-Type on the Azure function?
p.s. I'm not a Nodejs developer.

EDIT:
Unfortunately the blob output binding for node does not support setting a content type. One option would be to drop the output binding and use the azure storage sdk natively in your node function which should give you the control you need.
If using an Http trigger and output binding:
An express-like 'res' object can be accessed via content.res, so instead of stream.set you'll want context.res.set / context.res.type. The stream object returned in the getBuffer callback is a buffer, not a stream, and has nothing to do with the http response.
One thing to note is that azure functions does not support returning of streams from node yet - you'll need to have the entire buffer (which, luckily, getBuffer appears to return!)
Here is a getBuffer callback:
function(err, buffer){
if (err) {
context.log("There was an error processing the image.");
context.done(err);
}
else {
context.log("Successfully processed the image");
// set content type to Jimp.MIME_JPEG
context.res.type(Jimp.MIME_JPEG)
// send the raw response (don't apply any content negotiation)
context.res.raw(buffer);
}
});

Related

Getting XMLHttpRequest error from HTTP GET request on unformatted JSON

I'm trying to get JSON-data via HTTP from my Dart/Flutter function:
Future<List<News>?> getNews() async {
var client = http.Client();
var uri = Uri.parse('http://localhost:3000/news');
var response = await client.get(uri);
if (response.statusCode == 200) {
var jsonFile = response.body;
try {
return newsFromJson(jsonFile);
} catch (e) {
print(e);
}
}
return null;
}
The Json-File looks like this:
{
"news": [
{
"id": 0,
"title": "Test",
"text": "Test",
"buttonText": "Test",
"source": "Test",
"showButton": false,
"openFile": false,
"openWebsite": true
},
{
"id": 1,
"title": "Test",
"text": "Test",
"buttonText": "Test",
"source": "Test",
"showButton": false,
"openFile": false,
"openWebsite": true
}
]
}
When I start the following Script for the server that is going to provide the data, everything works fine but the json-data is NOT formatted when I call it in the browser:
const express = require('express');
const fs = require('fs');
const app = express();
app.get('/news', (req, res) => {
console.log('Received request');
fs.readFile('data.json', (err, data) => {
if (err) throw err;
const news = JSON.parse(data).news;
res.json(news);
});
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
The request from my dart code reaches the NodeJS-Script but ends with the mentioned XMLHttpRequest error. And here comes the interesting thing: When I use the tool json-server (https://github.com/typicode/json-server) with the same json-file, everything IS formatted when calling the url in browser and my Flutter/Dart codes work without any error. So in conclusion: The NodeJS-Script is working like the json-server tool. The only difference is, that the json provided by the NodeJS script isn't formatted in the browser which might causes the error.
Where is the problem?
Could be useful
List<News> newsFromJson(String str) =>
List<News>.from(json.decode(str).map((x) => News.fromJson(x)));
Error: XMLHttpRequest error.
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 299:10 createErrorWithStack
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 341:28 _throw
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/core/errors.dart 116:5 throwWithStackTrace
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/zone.dart 1378:11 callback
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/schedule_microtask.dart 40:11 _microtaskLoop
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/schedule_microtask.dart 49:5 _startMicrotaskLoop
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 166:15 <fn>
Express.js will try to be compact about the data it sends, rather than pretty.
Format it explicitly:
res.set('Content-Type', 'application/json')
res.send(JSON.stringify(news, undefined, 2));
If you want the JSON data to be formatted exactly as it is in the file, and send the whole file, not a specific part of the JSON, just don't parse it:
res.set('Content-Type', 'application/json');
res.send(data);

How to write azure functions which will use express api

I have a azure function,
In index.js i have the following code
module.exports = function (context, req) {
const createHandler = require('azure-function-express').createHandler;
const app = require('express')();
app.get("/home", (req, res) => {
const y = { "name": "name", "dob": "ddmmyyyy" }
context.res = y
context.done()
});
module.exports = createHandler(app);
context.done();
};
i have function.json :
{
"bindings": [
{
"authLevel": "anonymous",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"route": "{*segments}"
},
{
"type": "http",
"direction": "out",
"name": "res"
}
],
"disabled": false
}
i have the above files in my azure function but i am not able get any output i just a blank page if i hit the api end point.
i have to use express to handle many other end points is there any way to handle in azure functions.
when i use nodejs local application setup, i am able to use express and handle many api end points with in a single module is that possible in azure functions? or i have to use different functions for each end point
See code below. We can use Azure function as a common express app.
const createHandler = require('azure-function-express').createHandler;
const app = require('express')();
app.get("/home", (req, res) => {
res.json({ "name": "name", "dob": "ddmmyyyy" });
});
app.get("/work", (req, res) => {
res.json({ "name": req.query.name, "dob": "ddmmyyyy" });
});
module.exports = createHandler(app);
module.exports = function (context, req) and context.done() are no longer useful if azure-function-express is in use. If you want to use other method of context, use req.context instead. See azure-function-express module doc.
Besides, Azure function has a prefix "api" in route by default, if you don't need it(like code above), change it to empty your host.json.
If your function runtime is ~2(beta).
{
"version": "2.0",
"extensions": {
"http": {
"routePrefix": ""
}
}
}
Else in ~1
{
"http": {
"routePrefix": ""
}
}
I have also use try this azure-function-express package but its still in the development and need a-lot of improvement. The best package i found is Azure AWS Severless Express
Package. Its very easy to use and compatible. You can easily use the Express with azure functions

HTTP request failing in AWS Lambda function for Alexa

I am trying to return data from an external api in my inline Lambda function but when I test this in the developer console for Alexa, I get 'There was a problem with the requested skills response' and I can't work out why.
Also, as I am doing this from the AWS console, I can't console.log to see what it actually being returned.
(I have removed the default intents for the sake of the post)
const request = require('request');
const handlers = {
'LaunchRequest': function () {
this.emit(':ask', 'Welcome');
},
'GiveUpdateIntent': function (){
var slot = this.event.request.intent.slots.line.value;
httpGet(slot, (theResult) => {
this.response.speak(theResult);
this.emit(':responseReady');
});
}
};
function httpGet(query, callback) {
var options = {
host: 'api.tfl.gov.uk',
path: '/line/' + encodeURIComponent(query) + '/status',
method: 'GET',
};
var req = http.request(options, res => {
res.setEncoding('utf8');
var responseString = "";
//accept incoming data asynchronously
res.on('data', chunk => {
responseString += chunk;
});
//return the data when streaming is complete
res.on('end', () => {
console.log(responseString[0]);
callback(responseString[0]);
});
});
req.end();
}
exports.handler = function (event, context, callback) {
const alexa = Alexa.handler(event, context, callback);
alexa.APP_ID = APP_ID;
alexa.registerHandlers(handlers);
alexa.execute();
};
"There was a problem with the requested skills response" generally means that the response from your skill was not in the expected format.
Your API request
Ex: vicotria
https://api.tfl.gov.uk/Line/victoria/Status
returns a JSON, and you can't directly pass it Alexa as response. Before you send it back to Alexa, take out status that you actually want Alexa to speak. Then put that into a meaningful sentence that any skill user will understand and send it back.
For example you can return something like:
var speech = "Status severity description for " +
this.event.request.intent.slots.line.value +
" is "
+ responseBody[0].lineStatuses.statusSeverityDescription;
this.emit(':ask',speech, "your re-prompt here");
This is a sample JSON that I got
[
{
"$type": "Tfl.Api.Presentation.Entities.Line, Tfl.Api.Presentation.Entities",
"id": "victoria",
"name": "Victoria",
"modeName": "tube",
"disruptions": [],
"created": "2018-07-31T12:11:08.477Z",
"modified": "2018-07-31T12:11:08.477Z",
"lineStatuses": [
{
"$type": "Tfl.Api.Presentation.Entities.LineStatus, Tfl.Api.Presentation.Entities",
"id": 0,
"statusSeverity": 10,
"statusSeverityDescription": "Good Service",
"created": "0001-01-01T00:00:00",
"validityPeriods": []
}
],
"routeSections": [],
"serviceTypes": [
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Regular",
"uri": "/Line/Route?ids=Victoria&serviceTypes=Regular"
},
{
"$type": "Tfl.Api.Presentation.Entities.LineServiceTypeInfo, Tfl.Api.Presentation.Entities",
"name": "Night",
"uri": "/Line/Route?ids=Victoria&serviceTypes=Night"
}
],
"crowding": {
"$type": "Tfl.Api.Presentation.Entities.Crowding, Tfl.Api.Presentation.Entities"
}
}
]
CloudWatch:
Always make use of CloudWatch to see the logs of your Lambda function, you will get a link under Monitoring tab of your Lambda Function.
Configuring Lambda Test Events: You can test you Lambda code right from your inline editor by configuring Lambda Test Events under Test menu of your inline editor. A function can have up to 10 test events.
This is because your handler is returning before the callback is called. I strongly suggest to move away from callback based development in NodeJS and to use Promise instead.
I just answered a similar question, and provided sample code with promises. Check it here How to make an asynchronous api call for Alexa Skill application with a Lambda function?
The issue turned out to be with using http itself instead of https.
The only response back that I was getting was a status code of 302 which is a redirection because the api I was calling changes all http requests to https.
Therefore, I changed my import to https and used the https.get method (instead of http.get) to call the api and the correct response was returned.

Render raw image bytes to response body

I'm creating an API that creates authorized API calls to Google's APIs, specifically Drive for this question. My API is working fine and uses Google's Node API to make the requests. When I fire off a request to this resource, I get back the following response:
{
"kind": "drive#file",
"id": "...",
"name": "bookmobile.jpg",
"mimeType": "image/jpeg"
}
I use the above response to determine the MIME type of the file I'm to display later. I then make a subsequent call to the same endpoint, but specifying alt=media as an option to download the file as specified in Google's Guide. If I console.log or res.send() the response, I get the following output:
Which we can see is the raw image bytes from the API call. How do I render these bytes to the response body properly? My code is as follows:
// DriveController.show
exports.show = async ({ query, params }, res) => {
if (query.alt && query.alt.toLowerCase().trim() === 'media') {
// Set to JSON as we need to get the content type of the resource
query.alt = 'json'
// Get the Files Resource object
const options = createOptions(query, params.fileId)
const filesResource = await Promise.fromCallback(cb => files.get(options, cb))
// Grab the raw image bytes
query.alt = 'media'
await createAPIRequest(createOptions(query, params.fileId), 'get', res, filesResource)
} else {
await createAPIRequest(createOptions(query, params.fileId), 'get', res)
}
}
async function createAPIRequest (options, method, res, filesResource = {}) {
try {
const response = await Promise.fromCallback(cb => files[method](options, cb))
if (filesResource.hasOwnProperty('mimeType')) {
// Render file resource to body here
} else {
res.json(response)
}
} catch (error) {
res.json(error)
}
}
Searching through various answers here all seem to point to the following:
res.type(filesResource.mimeType)
const image = Buffer.from(response, 'binary')
fs.createReadStream(image).pipe(res)
But this kills my Express app with the following error:
Error: Path must be a string without null bytes
How would I go about rendering those raw image bytes to the response body properly?
The Google API client returns binary data as a string by default, which will corrupt image data when it happens. (The issue is discussed on this thread: https://github.com/google/google-api-nodejs-client/issues/618). To fix, use the encoding: null option when requesting the file contents:
files[method](options, { encoding: null }, cb))

AWS lambda function- 'An error has occurred: Received error response from Lambda: Handled'

Working on AWS Lex for creating a ChatBot and using the Node.js in AWS Lambda.
Error: An error has occurred: Received error response from Lambda:
Handled
Lambda function:
var aws = require('aws-sdk');
var ses = new aws.SES({region: 'us-east-1'});
exports.handler = function(event, context, callback) {
var eParams = {
Destination: {
ToAddresses: [event.currentIntent.slots.Email]
},
Message: {
Body: {
Text: {
Data: "Hi, How are you?"
}
},
Subject: {
Data: "Title"
}
},
Source: "abc#gmail.com"
};
var email = ses.sendEmail(eParams, function(err, data){
if(err)
else {
context.succeed(event);
}
});
};
How to get a proper response from Lambda to Lex after successful execution (Email Service works properly). I have tried context.done(); but it did not worked out.
Edit 1:
Tried adding below response test from AWS Documentation for LEX still getting the same error response.
exports.handler = (event, context, callback) => {
callback(null, {
"dialogAction": {
"type": "ConfirmIntent",
"message": {
"contentType": "PlainText or SSML",
"content": "message to convey to the user, i.e. Are you sure you want a large pizza?"
}
}
});
As mentioned in the lambda-input-response-format docs here fulfillmentState property is required in the response.
Other thing is you have to pass either PlainText OR SSML for the contentType in the response. In your case its just PlainText.
exports.handler = (event, context, callback) => {
callback(null, {
"dialogAction": {
"type": "ConfirmIntent",
"fulfillmentState": "Fulfilled", // <-- Required
"message": {
"contentType": "PlainText",
"content": "message to convey to the user, i.e. Are you sure you want a large pizza?"
}
}
});
The above code should solve your problem.
However if you see the req-res in the network tab you would receive HTTP Error 424 which says DependencyFailedException which says "Amazon Lex does not have sufficient permissions to call a Lambda function" very misleading.

Resources