i send a file from "react dropzone component" to "node server" and try to upload this with "multer" but no show any error, the file no upload and req.file/s is undefined
var express = require('express');
var router = express.Router();
var msg = require('../helpers/MessageHandler');
var CM = require('../helpers/ContentMessages.json');
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, 'public/uploads/');
},
filename: function(req, file, cb) {
cb(null, Date.now() + file.originalname);
}
});
var upload = multer({storage: storage}).any();
var path = '/enterprise';
router.post(path, function(req, res, next) {
var enterprise = req.body.obj;
console.log(req.body);
console.log(req.files);
console.log(req.file);
upload(req, res, function(err) {
if(err) {
return res.status(500).json(msg.prototype.errorMsg(err));
} else {
return res.status(200).json(msg.prototype.success(CM.message.success.doc_create, null));
}
});
});
the react component is somthing like this,
in the fetch function i send a object with all the fields
insertObj (values) {
console.info(values);
const obj = JSON.stringify({obj: values});
let url = '/api/v1/enterprise';
const headers = { 'Content-Type': 'application/json', 'Access-Control-Request-Method': '*'};
const req = new Request(url, {method: 'POST', headers: headers, body: obj});
fetch(req)
.then((response) => {
return response.json();
})
.then((enterprise) => {
console.log(enterprise);
}).catch((error) => {
console.log(error);
});
}
inside multer req.files will be visible. So change your code to this:
upload(req, res, function(err) {
var enterprise = req.body.obj;
console.log(req.body);
console.log(req.files);
console.log(req.file);
if(err) {
return res.status(500).json(msg.prototype.errorMsg(err));
} else {
return res.status(200).json(msg.prototype.success(CM.message.success.doc_create, null));
}
});
Also instead of 'Content-Type': 'application/json' there should be enctype='multipart/form-data'.
Related
I am using Multer to save images but I need to get the path of the image to save it to MongoDB. I am trying to get the path with req.file but it always tells me on the console that it is undefined.
this is my route:
import { Router } from 'express';
import { check, validationResult } from 'express-validator';
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/products')
},
filename: function (req, file, cb) {
cb(null, new Date().toISOString().replace(/:/g, '-') + file.originalname)
}
});
const fileFilter = (req, file, cb) => {
// reject a file
if (file.mimetype === 'image/jpeg' ||file.mimetype === 'image/png') {
cb(null, true);
} else {
cb(null, false);
//cb(new Error('I don\'t have a clue!'))
}
}
const upload = multer(
{ storage: storage,
limits:{
fileSize: 1024 * 1024
},
fileFilter: fileFilter
});
let router = Router();
router.post('/', upload.single('img'),
newProduct
);
And in the new Product controller I am trying to read the req.file but the console tells me that it is undefined:
Controller:
import { Product } from '../models'
let newProduct = async (req, res = response ) => {
console.log('file ' + req.file); //UNDEFINED
try {
let { status, user, ...body } = req.body;
let productDB = await Product.findOne ( { 'name': body.name } );
if (productDB) {
return res.status(400).json({
msg:`El producto ${ productDB.name } ya existe`
})
}
let data = {
...body,
name: body.name,
user: req.user._id
}
let product = new Product( data );
await product.save();
res.status(200).json( product );
} catch (error) {
return res.status(400).json({
error
});
}
}
Console:
Thanks for your help.
you can try to do this in filename instead:
filename: function (req, file, cb) {
req.imageName = new Date().toISOString().replace(/:/g, '-') + file.originalname
cb(null, req.imageName)
}
then there:
console.log('file ' + req.file); //UNDEFINED
//you can get imageName instead
console.log('imageName',req.imageName)
//if you want url to store in database you can do this
//supposing your have images directory in root of your node server
const url = `${req.protocol}://${req.get('host')}/images/${req.body.image}`
From what I know I should modify the post function, but I don't know exactly how to add the new json object to the JSON file. What I would want to happen to the JSON file after the post function is having a 4th element, just like the others already existing 3.
The JSON file:
[
{
"author": "John",
"comment": "How are you"
},
{
"author": "Alex",
"comment": "Hello"
},
{
"author": "Maria",
"comment": "Good morning"
}
]
Node js:
const express = require('express')
const app = express()
const port = 3000
var someObject = require('./bd.json')
app.use(express.json())
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, access-control-allow-origin")
next();
});
app.get('/', (req, res) => res.send('Hello World!'))
app.post('/', (req, res) => {
res.send({Status: 'OK'});
})
app.get('/comments', (req, res) => {
res.send(someObject);
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
EDIT: I added the HTML part
HTML:
<html>
<head>
<script>
window.onload = function(){
getComments();
}
function getComments(){
fetch("http://localhost:3000/comments")
.then((data) => { return data.json() })
.then((json) => displayComments(json))
}
function displayComments(data){
let responseArea = document.getElementById('responseArea');
for (let i = 0; i<data.length; i++){
let authorName = document.createElement('P');
authorName.innerText = data[i]["author"];
let commentContent = document.createElement('P');
commentContent.innerText = data[i]["comment"];
let someRespone = document.createElement('DIV')
someRespone.appendChild(authorName)
someRespone.appendChild(document.createElement('BR'))
someRespone.appendChild(commentContent);
someRespone.style.border = "1px solid black";
responseArea.appendChild(someRespone);
}
}
function sendInformation(){
let name = document.getElementById('name').value;
let comment = document.getElementById('comment').value;
fetch("http://localhost:3000", {
method: 'POST',
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json'
// 'Content-Type': 'application/x-www-form-urlencoded',
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer', // no-referrer, *client
body: JSON.stringify({name: name, comment: comment})
}).then((data) => {
return data.json()
}).then((json)=>{
if(json.Status === 'OK'){
document.getElementById('responseArea').innerText='Information receieved';
} else {
document.getElementById('responseArea').innerText='Information was not received';
}
console.log(json);
})
}
</script>
</head>
<body>
Name:
<input id='name' type='text' placeholder="name"/>
</br>
Comment:
<textarea id='comment'> </textarea>
<input type='button' value="Send" onClick="sendInformation()">
<div id='responseArea'></div>
</body>
</html>
You have to read the file first convert it to an object then back to json and write to the file system. Here is an example with your code.
app.post('/', (req, res) => {
fs.readFile('file.json', 'utf8', (err, data) => {
if (err) {
console.log(err);
} else {
let obj = JSON.parse(data);
obj.push({
author: req.body.author,
comment: req.body.comment
}); //add some o
let json = JSON.stringify(obj);
fs.writeFile('file.json', json, 'utf8', (err) => {
if (err) {
throw err
}
console.log('the file has been saved')
res.send("succes")
});
}
})
})
You can use node fs module to read and write your json data into json file.
First you need to read all the comments from the file which includes old comments.
Then convert that to Object and append the new comment and save.
Sample Code
const fs = require('fs');
const path = require('path');
const express = require('express')
const app = express()
const port = 3000
const someObject = path.join(__dirname, 'bd.json');
app.use(express.json())
app.use(function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*"); // update to match the domain you will make the request from
res.header("Access-Control-Allow-Headers", "Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers, access-control-allow-origin")
next();
});
app.get('/', (req, res) => res.send('Hello World!'));
app.post('/', (req, res) => {
/* Sample code */
fs.readFile(someObject, { encoding: 'utf-8' }, function (err, data) { // Read the old comments
if (!err) {
const comments = JSON.parse(data);
if (comments && comments.length > 0) {
comments.push(req.body);
} else {
comments = [];
}
fs.writeFile(someObject, comments, (err) => { // Write back the all comments with the newly added comment.
// throws an error, you could also catch it here
if (err) throw err;
// success case, the file was saved
console.log('saved!');
res.send({ Status: 'OK' });
});
} else {
console.log(err);
throw err;
}
});
/* Sample code */
})
app.get('/comments', (req, res) => {
res.send(someObject);
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
I have an express server that gets a list of podcasts, from an endpoint.
This apart works fine, but there is a token that I use in requests to authorize entry to the endpoints.
the response when gaining an access token looks like:
{ access_token: '8c9d31761cbd32da25f1f1b988b527cde01c9d8a',
expires_in: 604800,
token_type: 'Bearer',
scope: 'podcast_read episode_read podcast_update episode_publish' }
I have a refresh token that I use when refreshing the token and works well.
The way I'm doing it at the moment is, I have a text file that holds the token, the app reads from this when making a request, I have set up a function, that is called every time the podcasts route is called router.get('/podcasts', checkIfTokenValid, (req, res, next) => { to check if the token is valid or expired if so, refresh the token and write the new token to the file.
The only thing about this is; the write to file function is executed after the podcasts route connects to the endpoint, so the old access token is used.
Logging to the console, the functions are executed before the podcasts route gets all the podcasts, except for the writeAccessTokenToFile() function.
Just wondering, is there a better way to do this?
var express = require('express');
var router = express.Router();
var app = express();
var path = require('path');
var fs = require('fs');
const request = require('request');
var refreshToken = '425153ed4ddb4aee5sjsjsfaeffc46ab9944aece0400f';
var clientId = 'myId';
var client_secret = 'secret';
var isAccessTokenValid;
var access_token_file = path.join(__dirname, 'access_token.txt');
function refreshAccessToken() {
console.log('refreshAccessToken')
var body = { 'grant_type': 'refresh_token', 'refresh_token': refreshToken }
var options = {
url: `https://api.podbean.com/v1/oauth/token`,
headers: { 'Authorization': 'Basic ' + new Buffer(clientId + ":" + client_secret).toString('base64') },
json: body
}
request.post(options, (err, response, body) => {
// console.log(body.expires_in*1000)
if (err) {
return response.status(500).json({
title: 'An error has occured',
error: err
})
}
console.log(body)
writeAccessTokenToFile(body.access_token);
})
}
function getAccessToken() {
return fs.readFileSync(access_token_file, 'utf8');
}
function writeAccessTokenToFile(token) {
console.log('writeAccessTokenToFile = '+ token)
var data = getAccessToken();
var result = data.replace(data, token);
fs.writeFileSync(access_token_file, result, 'utf8');
}
function checkIfTokenValid (req, res, next) {
console.log('checkIfTokenValid')
var options = {
url: `https://api.podbean.com/v1/oauth/debugToken?access_token=${getAccessToken()}`,
headers: { 'Authorization': 'Basic ' + new Buffer(clientId + ":" + client_secret).toString('base64') }
}
request(options, (err, response, body) => {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
})
}
// console.log(JSON.parse(body))
isAccessTokenValid = JSON.parse(body).is_valid;
if (isAccessTokenValid) {
refreshAccessToken();
}
next();
})
};
router.get('/podcasts', checkIfTokenValid, (req, res, next) => {
var options = {
url: `https://api.podbean.com/v1/podcasts?access_token=${getAccessToken()}`
}
request(options, (err, response, body) => {
if (err) {
return res.status(500).json({
title: 'An error has occured',
error: err
})
}
res.json(JSON.parse(body));
next();
})
});
module.exports = router;
I setup multer like this
let multer = require('multer');
let apiRoutes = express.Router();
let UPLOAD_PATH = '../uploads';
let storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, UPLOAD_PATH);
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now());
}
});
let upload = multer({ storage: storage });
and in route I am getting data and an image and use multer like!
apiRoutes.post('/update', passport.authenticate('jwt', { session: false }), (request, response) => {
let record = {
name: request.body.name,
location: request.body.location,
about: request.body.about,
userid: request.body.userid,
avatar: request.body.filename
};
let userData = {
name: request.body.name
};
if (request.body.filename) {
upload(request, response, (error) => {
});
}
profile.findOneAndUpdate({ userid: request.body.userid }, record, {new: true}, (error, doc) => {
if (error) response.json(error);
user.findOneAndUpdate({ _id: request.body.userid }, record, (error, result) => {
if (error) throw error;
response.json(doc);
});
});
});
What is happening with this code is that when I do not send an image to backend then I get data from front end and store it into database. But when I send image along side data then it return POST /api/1.0/profile/update 401 0.396 ms - -.
It means I am not getting any data at all. Whats wring with the code here?
You can't use Multer in your /update route. Use Multer in your router like this:
var upload = multer({ dest: 'uploads/' })
apiRoutes.post('/profile', upload.single('image'), function (req, res, next) {
// Uploaaded
})
if you add it and still can't get our file, you should update your form with this parameter: enctype="multipart/form-data"
I have this express route with multer file-upload. When the upload is complete, I would like to encode the image to base64 and send with response.
However when I do it like this, the code tries to execute the base64 encoding before the file is created to the folder.
Edit: Added storage & upload functions
const storage = multer.diskStorage({
destination: (req, file, callback) => {
if (!fs.existsSync('./uploads')) {
fs.mkdirSync('./uploads');
}
let path = './uploads';
callback(null, path);
},
filename(req, file, cb) {
let fileExt = file.originalname.substring(file.originalname.lastIndexOf('.')).toLowerCase();
if (!imageFilter(fileExt)) {
return false;
} else {
cb(null, file.originalname);
}
},
onError: function (err, next) {
console.log('error', err);
next(err);
},
});
const upload = multer({
storage,
limits: {
fileSize: 1000 * 1000 * 2 // 2 MB
}
}).single('file');
router.post('/upload', function (req, res) {
var directory = 'uploads';
fs.readdir(directory, (err, files) => {
if (err) throw err;
for (var file of files) {
fs.unlink(path.join(directory, file), err => {
if (err) throw err;
});
}
});
upload(req, res, function (err) {
if (err) {
return res.status(404).json({
success: false,
message: 'File is too large. (Max 2MB)'
});
}
var file = req.file;
var base64str = base64_encode('./uploads/' + file.originalname);
return res.status(200).json({
success: true,
url: 'http://' + ip.address() + ':' + constants.PORT + '/api/uploads/' + file.originalname,
image: 'data:image/png;base64,' + base64str
});
});
});
What would be the smartest way to achieve the right order of operations. Possibly promises or async/await?
This solution worked for me :
Node v8.4.0 is required for this
//app.js
const fs = require('fs');
const express = require('express');
const cors = require('cors');
const bodyParser = require('body-parser');
const app = express();
app.use(cors({credentials: true, origin: 'http://localhost:4200'}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const Uploader = require('./Uploader.js');
const uploader = new Uploader();
app.post('/upload', uploader.startUpload);
//Uploader.js
const util = require("util");
const crypto = require("crypto");
const multer = require('multer');
class Uploader {
constructor() {
const storageOptions = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, __dirname + '/uploads/')
},
filename: function(req, file, cb) {
crypto.pseudoRandomBytes(16, function(err, raw) {
cb(null, raw.toString('hex') + Date.now() + '.' + file.originalname);
});
}
});
this.upload = multer({ storage: storageOptions });
}
async startUpload(req, res) {
let filename;
try {
const upload = util.promisify(this.upload.any());
await upload(req, res);
filename = req.files[0].filename;
} catch (e) {
//Handle your exception here
}
return res.json({fileUploaded: filename});
}
}
Edit :
The library "util" provide you a "promisify" method which will give you the possibility to avoid something called the "callback hell". It converts a callback-based function to a Promise-based one.
This is a small example to understand my code above:
const util = require('util');
function wait(seconds, callback) {
setTimeout(() => {
callback();
}, seconds);
}
function doSomething(callType) {
console.log('I have done something with ' + callType + ' !');
}
// Default use case
wait(2 * 1000, () => {
doSomething('callback');
});
const waitPromisified = util.promisify(wait);
// same with promises
waitPromisified(2000).then((response) => {
doSomething('promise');
}).catch((error) => {
console.log(error);
});
// same with async/await
(async () => {
await waitPromisified(2 * 1000);
doSomething('async/await');
})();
Will print :
I have done something with callback !
I have done something with promise !
I have done something with async/await !