ajax not able to capture response from Node Express in multer - javascript

I am using multer to upload image and it works great, I am able to see the data getting printed to console, but I am not sure how to capture this data and manipulate it in browser. Below is my code:
var multer = require("multer");
var storage = multer.diskStorage({
destination: function(req, file, callback){
callback(null, 'uploads'); // set the destination
},
filename: function(req, file, callback){
callback(null, 'FILE1' + '.jpg'); // set the file name and extension
}
});
var upload = multer({storage: storage});
app.post('/', upload.any(), function(req, res, next) {
console.log(req.files);
var image = req.files;
console.log(image);
res.send(image); //this should come to jquery response object in browser, but not sure why it is not
});
and below is my jquery code that should be able to get above data in response object:
$('body').on('click', '#uploadFile', function (event) {
$.ajax({
type: 'POST',
url: '/',
success: function (response) { //this is always "" not sure why, I am expecting multer response to be here
console.log('successfully uploaded', response);
},
error: function () {
console.log('coudn\'t upload');
}
});
//return false;
})
I think the problem is that by the time I capture response in jquery, multer hasnt uploaded the file so I have no or blank response in ajax. I am not sure how to fix this.

app.post('/', upload.any(), function(req, res, next) {
console.log(req.files);
var image = req.files;
console.log(image);
res.send(image); // this should come to jQuery response object
res.end(); // !!! - note this!
});
You have to add res.end() to finalize the response.

Related

Uploading a file to nodeJS server

Client code:
var data = new FormData();
data.append(fileName, blob, 'test.html');
fetch('http://localhost:3000/', {
method: 'POST',
headers: {
},
body: data
}).then(
response => {
console.log(response)
}
).then(
success => {
console.log(success)
}
).catch(
error => {
console.log(error)
}
);
Server code:
router.post('/', urlencodedParser, function(req, res, next) {
const body = req.body;
console.log(body);
res.send(`You sent: ${body} to Express`);
});
I am sending a blob in the body of a post request. When I send it to the server I want the server to download the file from the body of the request. How can i download this file? Or is there a simpler way to upload from client?
If you can utilize an NPM package formidable, there appears to be a solution at: https://www.w3schools.com/nodejs/nodejs_uploadfiles.asp
Once you have the file received, you can use the fs module to save and store in server
May it can solve your problem.
const fs = require('fs');
let directory = '/temp/data'; // where you want to save data file
router.post('/', urlencodedParser, function(req, res, next) {
const body = req.body;
console.log(body);
fs.writeFile(directory, body, function(err) {
if(err) {
return console.log(err);
}
console.log("File has been saved");
});
res.send(`You sent: ${body} to Express`);
});
This solved my answer - https://attacomsian.com/blog/uploading-files-nodejs-express, which basically uses a middleware to do the upload.
This was basically like:
const x = 6;
console.log(x);
Error: value is f'd up
const x = 6;
magic.valueParse(x);
console.log(x);
6
Also, i would like to point out how bodyParser cannot be used for multipart data. It is mentioned on the official docs, but even responses I get seem to point to bodyParser. So I thought I'd re-iterate that.

NodeJS same results for two different requests

SOLVED THANKS TO #Patrick Evans
I am creating my own web project and i need some help.
At the website, the client is requested to upload a face photo.
Then , when the client presses "upload" button , his photo is sent with a request to "face++" api which gives back details about the photo such as emotions and gender, at a different ejs page. At the new page the client sees his photo and below are the details about his photo.
It works fine , but when the client gets back to the homepage, and chooses a different new photo, then presses upload , he sees his new photo that he chose, but gets the same details as were at the last photo (details from face++ api).
I use the following:
express.
unirest for making the request to "face++" api.
cloudinary for having a url, and using the url at the face++ request(the url represents the client's photo).
multer for storing at local drive.
When i print out the details which return from "face++" api , at the "requestFromApi.end()" function , i already notice the details havent changed from prior request, but i do notice at cloudinary that a different photo was uploaded.
I attached my app.js code.
Thanks alot for any help :)
var unirest = require("unirest");
var requestFromApi = unirest("POST", "https://faceplusplus-
faceplusplus.p.rapidapi.com/facepp/v3/detect");
var cloudinary = require("cloudinary").v2;
const express = require('express');
const multer = require('multer');
const app = express();
const path = require("path");
var bodyParser = require("body-parser")
app.use(bodyParser.json({ limit: '50mb' }));
app.use(bodyParser.urlencoded({ // to support URL-encoded bodies
limit: '50mb',
extended: true
}));
app.set("view engine", "ejs");
cloudinary.config({
cloud_name: 'dkqvnprcj',
api_key: '756886954695832',
api_secret: 'you know i cant give you that...'
});
app.get("/", function (req, res) {
res.render("FaceApp.ejs");
});
// SET STORAGE
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now() +
path.extname(file.originalname));
}
})
var upload = multer({ storage: storage })
app.post('/upload', upload.single('photo'), (req, res) => {
if (req.file) {
cloudinary.uploader.upload(req.file.path, function (error, result) {
//console.log(req.file);
let result_ = result;
let url = result.url;
//console.log(url)
requestFromApi.query({
return_attributes: "gender,age,smiling,facequality,eyestatus,emotion,ethnicity,beauty,skinstatus",
image_url: url
});
requestFromApi.headers({
"x-rapidapi-host": "faceplusplus-faceplusplus.p.rapidapi.com",
"x-rapidapi-key": "9dd7fa4266mshf1c29ba307ecf2dp1bb1dajsna431d00b6273",
"content-type": "application/x-www-form-urlencoded"
});
requestFromApi.form({});
requestFromApi.end(function (result) {
if (result.error) throw new Error(result.error);
else {
let detailsFromApi = JSON.parse(JSON.stringify(result.body.faces));
detailsFromApi.forEach(function (element) {
console.log(element);
});
res.render("image",{result_ : result_, detailsFromApi:detailsFromApi});
}
});
});
}
else throw 'error';
});
SOLVED THANKS TO #Patrick Evans [1]: https://stackoverflow.com/users/560593/patrick-evans
I had to make sure i call "Unirest" at every single Post request, and not just at the beginning of the execution.

node js multer file upload not working. req.file and req.files always undefined

I am trying to upload a file to my server, but req.file and req.files is always undefined on my POST REST endpoint.
The content I'm trying to upload is a ".dat" file, and I am expecting a json response.
Here's my code:
Server side:
var express = require('express');
var multer = require('multer')
var upload = multer({ dest: 'uploads/' })
var app = express()
app.post('/creoUpload', upload.single('uploadFileField'), function(req, res, next) {
console.log("req.file = ",JSON.stringify(req.file)); //is undefined
console.log("req.files = ",JSON.stringify(req.files)); //also undefined
});
Client Side:
<form id="creoUploadForm" action="/creoUpload" enctype="multipart/form-data" method="post">
<input type='file' name='uploadFileField'><br><br>
<input type='submit' value='Upload'/>
</form>
JS:
$( "#creoUploadForm" ).submit(function( event ) {
event.preventDefault();
var formData = new FormData($(this)[0]);
$.ajax({
url: '/creoUpload',
type: 'POST',
data: formData,
async: true,
cache: false,
contentType: false,
processData: false,
success: function (returndata) {
console.log("RETURN DATA IS: ",returndata);
},
error: function (err) {
console.log("ERROR: ",err);
}
});
});
I keep playing around with the fields but it's not working.. anyone see what I'm doing wrong here?
I'm following the example from https://www.npmjs.com/package/multer
Versions:
Express Version: 4.12.0
Node Version: 6.5.0
JQuery: 1.12.1
Thank you!
You are using multer as a middleware, So before entering into your function and printing it has uploaded images to storage and removes record from req.files.
There are two ways you can access req.files.
Use multer as a function stated by finw3.
Another solution is:
//CODE STARTS
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/filepath')
},
filename: function (req, file, cb) {
let filename = 'filenametogive';
req.body.file = filename
cb(null, filename)
}
})
var upload = multer({ storage: storage })
//CODE ENDS
Now you can access files in req.body.file
I have the same issue, if you check the documentation on the Error Handling section there is another kind of implementation, that's the one that works for me. The code will be something like this:
var express = require('express');
var multer = require('multer');
var upload = multer({ dest: 'uploads/' });
var app = express();
app.post('/creoUpload', function(req, res, next) {
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
console.log('Err: ', err);
return;
} else {
console.log('req.file: ', JSON.stringify(req.file));
console.log('req.files: ', JSON.stringify(req.files));
return;
}
})
});
Don't set contentType to false, as POST method requires contentType = 'w-xxx-form-urlencoded', which is the JQ default.
If the req.file and req.files are undefined, then the problem is that multer did not receive the uploaded file and this issue seems related to jQuery.
Uploading a FormData with jQuery 'v1.12.1' is not supported and it won't send anything at all.
I will recommend to try with fetch Api or change the version of your jQuery to 3.3.1.
I already tested the code and I have successfully upload a file to my server when I change my jQuery version from v1.12.1 to v3.3.1.

Receiving "Uploading file too fast!" error from Imgur API

I created a node.js server that uses busboy to take requests, and pipe the files to Imgur for upload. However, I keep getting an "Uploading file too fast!" response from Imgur, and I'm not sure exactly what the problem is. Here is the code snippet involving busboy:
var express = require('express');
var Busboy = require('busboy');
var fs = require('fs');
var request = require('request-promise');
var router = express.Router();
router.post('/u', function(req, res, next) {
var busboy = new Busboy({headers: req.headers});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
if(fieldname == 'image') {
var options = {
uri: 'https://api.imgur.com/3/image',
method: 'POST',
headers: {
'Authorization': 'Client-ID ' + clientID // put client id here
},
form: {
image: file,
type: 'file'
}
};
request(options)
.then(function(parsedBody) {
console.log(parsedBody);
})
.catch(function(err) {
console.log(err);
});
}
});
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
console.log('field');
});
busboy.on('finish', function() {
res.status(200).end();
});
req.pipe(busboy);
});
As you can see I'm piping the request file directly into my request for imgur. Providing a ReadStream by simply saving the file to disc and then using fs.createReadStream() works perfectly, so I'm not really sure why trying to pipe directly from request to request gives me the error. The exact response I'm getting from Imgur is:
StatusCodeError: 400 - {"data":{"error":"Uploading file too fast!","request":"\/3\/image","method":"POST"},"success":false,"status":400}
If anyone has encountered this before, it would be helpful...
The first issue is that you should be using formData instead of form for file uploads. Otherwise, the request library won't send the correct HTTP request.
The second issue is that the stream object won't have the correct content length until it's fully processed. We can buffer the data ourselves and pass it after the initial file stream from busboy has processed.*
This gives us something that looks like
var express = require('express');
var Busboy = require('busboy');
var fs = require('fs');
var request = require('request-promise');
var router = express.Router();
router.post('/u', function(req, res, next) {
var busboy = new Busboy({headers: req.headers});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
if(fieldname == 'image') {
// the buffer
file.fileRead = [];
file.on('data', function(data) {
// add to the buffer as data comes in
this.fileRead.push(data);
});
file.on('end', function() {
// create a new stream with our buffered data
var finalBuffer = Buffer.concat(this.fileRead);
var options = {
uri: 'https://api.imgur.com/3/image',
method: 'POST',
headers: {
'Authorization': 'Client-ID ' + clientID // put client id here
},
formData: {
image: finalBuffer,
type: 'file'
}
};
request(options)
.then(function(parsedBody) {
console.log(parsedBody);
})
.catch(function(err) {
console.log(err);
});
});
}
});
busboy.on('field', function(fieldname, val, fieldnameTruncated, valTruncated) {
console.log('field');
});
busboy.on('finish', function() {
res.status(200).end();
});
req.pipe(busboy);
});
Code for the buffering is from http://thau.me/2014/02/nodejs-streaming-files-to-amazons3/
Lastly, you may want to consider using the request library, as the request-promise library discourages the use of streams. See the github repo for more details: https://github.com/request/request-promise

Node REST API Image rendering issue

I have an issue to display a simple image on a web page. I have a node backend for the APIs and a node web server for the view pages.
The code was working very well but I need to add a sort of gateway in-between (that will handle authentication in the future) and it broke the code.
I am using express and gridfs to store and retrive from mongo the files.
Here is the code
HTML/Angular page
<img id="image" ng-src="http:localhost:3000/api/files/images/{{data.image}}" alt="" />
Gateway (node)
var request = require('request');
//settings - retrive the url of the api backend micro-service
var url = require('./configApiGw').url_service_api_contents;
//api to retrive a file stored in mongo using gridfs
app.get('/api/files/images/:id', function(req, res, next){
var uri = url+'/api/files/images/:'+req.params.id;
request({
uri: uri,
method: "GET",
timeout: 100000,
followRedirect: true,
maxRedirects: 10
}, function(error, response, body) {
res.send(response.body);
});
});
Backend API
app.get('/api/files/images/:id', function(req, res, next){
//call function to read the file using gridfs. call back function
readFile(req, res, function(file){
console.log('success');
});
});
function readFile(req,res,callback){
var fileId = req.params.id;
//delete the ':' that is added by the gateway
if(fileId.charAt(0) === ':'){
fileId = fileId.slice(1);
}
// streaming from gridfs
var rstream = gfs.createReadStream({filename: fileId});
var bufs = [];
rstream.on('data', function (chunk) {
bufs.push(chunk);
});
// done reading the file
rstream.on('end', function () {
var fbuf = Buffer.concat(bufs);
var file = (fbuf.toString('base64'));
callback(file);
});
//error handling, e.g. file does not exist
rstream.on('error', function (err) {
console.log('An error occurred!', err);
console.log(err);
res.send(500,err);
});
rstream.pipe(res);
}
The image does not display but I get a 200 OK response from both API backend and gateway.
When I look at the image details on the browser, I see the following data:
- Location: http://localhost:3000/api/files/images/file.jpeg
- Type: text/html
- Size: Unknown (not cached)
What am I doing wrong? thanks a lot.
Edit with Alexandr inputs
Gateway (Node) V2
var request = require('request');
//settings - retrive the url of the api backend micro-service
var url = require('./configApiGw').url_service_api_contents;
app.get('/api/files/images/:id', function(req, res, next){
var uri = url+'/api/files/images/:'+req.params.id;
request({
uri: uri,
method: "GET",
timeout: 100000,
followRedirect: true,
maxRedirects: 10
}, function(error, response, body) {
res.set('Content-Type', response.headers['content-type']);
res.send(response.body);
});
});
Backend API V2
//api to retrive a file stored in mongo using gridfs
app.get('/api/files/images/:id', function(req, res, next){
//call function to read the file using gridfs. call back function
db.readFile(req, res, function(file){
//res.send(file);
console.log("success");
});
});
readFile = function(req,res,callback){
var fileId = req.params.id;
//delete the ':' that is added by the gateway
if(fileId.charAt(0) === ':'){
fileId = fileId.slice(1);
}
//setHeaders content type for the file
setHeaders(fileId, function(contentType){
res.writeHead('200',{'Content-Type':contentType});
// streaming from gridfs
var rstream = gfs.createReadStream({filename: fileId});
var bufs = [];
rstream.on('data', function (chunk) {
bufs.push(chunk);
});
// done reading the file
rstream.on('end', function () {
var fbuf = Buffer.concat(bufs);
var file = (fbuf.toString('binary'));
callback(file);
});
//error handling, e.g. file does not exist
rstream.on('error', function (err) {
console.log('An error occurred!', err);
console.log(err);
res.send(500,err);
});
rstream.pipe(res);
});
};
function setHeaders(fileId, callback){
var ext = path.extname(fileId);
var contentType = 'text/html';
if (ext === '.gif') {
contentType = 'image/gif';
}
if (ext === '.jpeg') {
contentType = 'image/jepg';
}
if (ext === '.png') {
contentType = 'image/png';
}
if (ext === '.jpg') {
contentType = 'image/jpg';
}
callback(contentType);
}
The result is still not good: the image is not displayed. BUT now, the content-type is correctly set.
adding here the headers (postman):
Access-Control-Allow-Headers → Origin, X-Requested-With, Content-Type, Accept
Access-Control-Allow-Origin → *
Connection → keep-alive
Content-Length → 82360
Content-Type → image/jepg; charset=utf-8
Date → Fri, 20 Nov 2015 10:15:55 GMT
ETag → W/"141b8-Ox5qDdvc3kZTunf0uqMVQg"
X-Powered-By → Express
UPDATE
Try to set encoding property to null in the request object:
app.get('/api/files/images/:id', function(req, res, next){
var uri = url+'/api/files/images/:'+req.params.id;
request({
uri: uri,
method: "GET",
timeout: 100000,
followRedirect: true,
encoding: null,
maxRedirects: 10
}, function(error, response, body) {
res.set('Content-Type', response.headers['content-type']);
res.send(response.body);
});
});
Also, set image content-type headers to your responses:
app.get('/api/files/images/:id', function(req, res, next){
//call function to read the file using gridfs. call back function
readFile(req, res, function(file){
res.set('Content-Type', 'image/jpeg'); //it can different, depends on the image
res.send(file);
});
});
Gateway:
app.get('/api/files/images/:id', function(req, res, next){
var uri = url+'/api/files/images/:'+req.params.id;
request({
uri: uri,
method: "GET",
timeout: 100000,
followRedirect: true,
maxRedirects: 10
}, function(error, response, body) {
res.set('Content-Type', response.headers['content-type']);
res.send(response.body);
});
});
Yes, I am already using a controller for all the other components of the page but I call directly from the html page the API to retrive the image. You're right, I could change that.
The gateway is here also because I have multiple API components (micro-service architecture) and so the gateway is a great way to abstract all these different components for the web client.
I would prefer to keep the gateway to follow the microservices architecture pattern.

Categories

Resources