Node-multer: fails to uploade a file (cannot read property error) - node.js

I try to upload a file to a server using express.js and multer. I cannot get it work, even with a very simple code, it keeps displying the same error :
"TypeError: Cannot read property 'name' of undefined"
I suspect I make some really stupid mistake, yet I cannot find out what's the problem. Thank you very much for your help !
index.html :
<!doctype html>
<html>
<head>
<title>File upload test</title>
</head>
<body>
<form id="uploadform" method="post" action="/upload" enctype="multipart/form-data">
<p><input type="file" name="img"></p>
<p><input id="submit_upload" type="submit" value="Submit"></p>
</form>
</body>
</html>
and on the server side (server.js)
var express = require('express');
var app = express();
var multer = require('multer');
var upload = multer({ dest: 'uploads/' })
app.get('/', function(req, res){
res.sendfile(__dirname + '/client/index.html');
});
app.post('/upload', upload.single('img'), function (req, res) {
//var form_description = req.body.description;
console.log(req.files.name);
// insert operations into database get placed here
res.redirect('/');
});
package.json :
{
"name": "upload-example",
"version": "0.0.0",
"description": "A file upload example",
"main": "server.js",
"repository": "",
"author": "",
"dependencies": {
"async": "~0.2.8",
"express": "^3.2.6",
"multer": "^1.1.0",
"socket.io": "~0.9.14"
}
}

1) You configure multer to accept single file, and single file will be stored in req.file.
2) Original filename stored in req.file.originalname.
3) Temp filename stored in req.file.filename.

Related

node js pass / send post data to html

Hello i'm new to node js and i am trying send / pass post data to html but i can't seem to get it to work And was hoping maybe someone could point me in the right direction on how i can.
Server code:
const express = require("express");
const bodyParser = require('body-parser');
const app = express();
app.use(express.static("public"));
app.use(bodyParser.urlencoded({ extended: true }));
app.get("/", (request, response) => {
response.sendFile(__dirname + "/views/index.html");
});
app.post('/sendInfo', (req, res) => {
try {
console.log(`firstname: ${req.body.firstname} lastname: ${req.body.lastname}.`);
var firstName = req.body.firstname,
lastName = req.body.lastname;
res.sendFile(__dirname + "/views/info.html", { fistname: firstName, lastname: lastName });
} catch (e) {
console.error("error", e);
}
});
const listener = app.listen(process.env.PORT, () => {
console.log("Your app is listening on port " + listener.address().port);
});
views/info.html code:
<html>
<head>
<title>My Site title</title>
</head>
<body>
<h1><%= fistname %></h1>
<h1><%= lastname %></h1>
</body>
</html>
Looks like you are using the EJS template engine. So, there are a number of things that are missing in your code.
You need to tell express that you are using EJS at the template engine
The view must have an extension of .ejs not .html
You should be using res.render() and pass in the template name and the JSON data which will be used in the template
Set up a Node.js project using npm init -y, then run npm install express ejs, then create the app.js file (code given below) and finally create the views/index.ejs file (code given below). The views directory should be at the same level as your node_modules directory.
// app.js
const express = require('express');
const bodyParser = require('body-parser');
const port = process.env.PORT || 3006;
const app = express();
app.use(express.static(__dirname + '/build'));
app.use(express.json())
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json({ extended: true }));
app.set('view engine', 'ejs');
app.get('/fn/:first_name/ln/:last_name', (req, res) => {
res.render('index', {
first_name: req.params.first_name,
last_name: req.params.last_name
});
});
app.listen(port, () => {
console.log(`App listening on port ${port}`);
});
// views/index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<div>First name is <%= first_name %></div>
<div>Last name is <%= last_name %></div>
</body>
</html>
Your package.json file must look something like this, use the start script
{
"name": "node-template",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"ejs": "^3.1.5",
"express": "^4.17.1"
}
}
Run the app using npm start
Finally, open up a browser and hit http://localhost:3006/fn/John/ln/Doe
If all goes well you will see an html rendered in the browser like this...
First name is John
Last name is Doe
Output:
Good luck.
Note: For the sake of simplicity I used GET instead of POST and used path params instead of request body. But the template engine works the same way.

express-validator and populating an input field after an error

I'm relatively new to node and express, and have come across a problem I can't seem to solve.
I've created a SSCCE which tries to take two inputs, name and height, and if there's a validation error render the same page again with those values.
If I enter '2.2.2' into the height field (obviously not a float), the value is not re-rendered. If I change the height input to type="text", the field is rendered with the previous value. All other fields of type text behave as expected.
I've put 'novalidate' on the form, but is this a quirk with the number input type? Or have I made a simple mistake?
(I'm also slightly confused as to why 'isFloat()' accepts '2' as valid)
Any help would be appreciated
app.js
const express = require('express');
const app = express();
const bodyParser = require('body-parser');
const { check } = require('express-validator/check');
const { validationResult } = require('express-validator/check');
app.use(bodyParser.urlencoded({ extended: false }));
app.set('view engine', 'ejs');
app.set('views', 'views');
app.get('/', (req, res, next) => {
res.render('test', {
pageTitle: 'Test',
hasError: false,
validationErrors: []
})
});
app.post('/',
[
check('name', 'Name must be at least 2 letters in length')
.isLength({min: 3}),
check('height', 'Height must be a float')
.isFloat()
] ,
(req, res, next) => {
const errors = validationResult(req);
if(!errors.isEmpty()){
console.log(errors.array());
return res.render('test', {
pageTitle: 'Error',
hasError: true,
validationErrors: errors.array(),
person: {
name: req.body.name,
height: req.body.height
}
})
} else {
res.send(`Person created: ${req.body.name}, height: ${req.body.height}`);
}
});
app.listen(3000);
test.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title><%= pageTitle %></title>
</head>
<body>
<div class="errorMessage"><%= validationErrors.length > 0 ? validationErrors[0].msg:'' %></div>
<form action="/" class="test-form" method="POST" novalidate>
<label for="name">Name</label>
<input type="text" name="name" id="name" value="<%= hasError ? person.name:'' %>">
<label for="height">Height</label>
<input type="number" name="height" id="height" value="<%= hasError ? person.height:'' %>">
<button class="submit">Submit</button>
</form>
</body>
</html>
package.json
{
"name": "test",
"version": "1.0.0",
"description": "",
"main": "app.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"ejs": "^2.6.1",
"express": "^4.16.4",
"express-validator": "^5.3.1"
}
}
I gave up on this. There didn't seem to be any way to get an input field of type number to submit an invalid value, even with novalidate.
So I just changed the type to text.

How to use Electron with an existing Express application

I read and tried samples like the one on https://github.com/frankhale/electron-with-express but still I don't understand how to convert an Express app into Electron
How can I use Electron with an existing Express application?
Take for example this Express application:
app.js
var express = require("express");
var app = express();
var request = require("request");
app.set("view engine", "ejs");
app.get("/", function(req, res) {
res.render("search");
});
app.get("/results", function(req, res){
var query = req.query.search;
var url = "https://yts.am/api/v2/list_movies.json?sort=seeds&limit=15&query_term='" + query + "'";
request(url, function(error, response, body){
var data = JSON.parse(body);
if(!error && response.statusCode == 200){
//res.send(data["data"]["movies"][0]["title"]);
res.render("results", {data: data});
//["movies"][0]["title"]
}
else
console.log(data);
});
});
app.listen(process.env.PORT, process.env.IP, function(){
console.log("IMDB server has started");
});
search.ejs
Search for a movie
<form action="results" method="GET">
<input type="text" placeholder="search term" name="search">
<input type="submit">
</form>
results.ejs
Results page
<ul>
<% data["data"]["movies"].forEach(function(movie){ %>
<li>
<strong><%= movie["title"]%></strong> - <%= movie["year"]%>
</li>
<% }) %>
</ul>
Search again
In the Electron main.js file, you should require your app.js file to start the Express application, then create a new instance of BrowserWindow and load the URL that your Express application is listening on.
Note that you'll either have to hard code the IP and PORT in Electron, or export them from Express and import them into the Electron main.js script.
./main.js
const { BrowserWindow, app } = require('electron')
require('./app.js')
let mainWindow = null
function main() {
mainWindow = new BrowserWindow()
mainWindow.loadURL(`http://localhost:3000/`)
mainWindow.on('close', event => {
mainWindow = null
})
}
app.on('ready', main)
./package.json
{
"name": "your-app-name",
"version": "1.0.0",
"description": "A description of your application",
"main": "main.js",
"scripts": {
"start": "electron ."
},
"repository": "https://github.com/link/to/repo",
"keywords": [ "some","keywords" ],
"author": "You",
"license": "Your License",
"dependencies": {
"ejs": "^2.6.1", // required for your specific example where `ejs` package is used
"electron": "^3.0.9",
"express": "^4.16.4"
}
}
Then you'll want to make sure that the node_modules directory is in the same directory as main.js and package.json.
Finally, you can start your Express/Electron application using:
> npm start
If your Express application is not in the same directory as Electron, you will have to set the views directory for express accordingly:
app.js
var path = require('path')
var request = require("request");
var express = require("express");
var app = express();
app.set('views', path.join(__dirname, '/views'));
...
Where /views is a directory relative to app.js that contains your views.

Uploading file with express-fileupload

I am trying to upload a file with express-fileupload and am having no luck getting it to work. I can get the file (in this case an image) to 'upload' in the sense that I can get the console to show an image uploaded with the correct folder.
startup.js
router.get('/upload', function(req, res) {
res.render('upload');
});
router.post('/upload', function(req, res) {
// The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
let startup_image = req.files.image;
// Use the mv() method to place the file somewhere on your server
startup_image.mv('/images' , function(err) {
if (err) {
console.log(err);
}
});
});
Then my html form is
<form ref='uploadForm'
id='uploadForm'
action='/upload'
method='post'
encType="multipart/form-data">
<input type="file" name="image" />
<input type='submit' value='Upload!' />
</form>
You are pointing the directory where the file would go to, but you are not giving it a file name. I would say let the user decide the file name for the client side and add it to the path.
<input name="userFileName" type="text">//userFilename Here</input>
var myFILENAME = req.body.userFilename
startup_image.mv('/images/'+myFILENAME+'.jpg', ..) //myFILENAME needs to be added here
Also please see Full Example in how to upload files with express-fileupload
UPDATE
I found solution to your problem you need to add __dirname to this line which will let the program know your current directory to your source code.
startup_image.mv(__dirname + '/images' , function(err) {..
UPDATE 2
Here is my source code, if you want you can try it with this.
my html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<form ref='uploadForm' encType="multipart/form-data" class="" action="/upload" method="post">
<input type="text" name="fileName" value=""><br>
<input type="file" name="foo" value=""><br>
<input type="submit" name="" value="upload!">
</form>
</body>
</html>
my main source
var express = require("express);
var app = express();
const fileUpload = require('express-fileupload');
//npm install ejs, express, express-fileupload
//middleware
app.use(express.static(__dirname));
app.set('view engine', 'ejs');
app.use(fileUpload());
app.get('/inputFile', function(req, res){
res.render('inputt');
});
app.post('/upload', function(req, res) {
// The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
var startup_image = req.files.foo;
var fileName = req.body.fileName;
// Use the mv() method to place the file somewhere on your server
startup_image.mv(__dirname + '/images/' + fileName + '.jpg' , function(err) {
if(err){
console.log(err);
}else{
console.log("uploaded");
}
});
});
app.listen(7777);
using async/await style
in your server file do this
const fileUpload = require('express-fileupload');
app.use(
fileUpload({
limits: { fileSize: 50 * 1024 * 1024 },
useTempFiles: true,
// dir for windows PC
tempFileDir: path.join(__dirname, './tmp'),
}),
);
then in your controllers, do this
const file = req.files.filename;
await file.mv(file.name);
if (!file || Object.keys(req.files).length === 0) {
return res.status(400).console.error('No files were uploaded.');
}
This solution is for non ejs and exporting modules solution:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>File Upload</title>
</head>
<body>
<form ref='uploadForm' encType="multipart/form-data" class="" action="/path/to/nodejs/upload/file" method="post">
<input type="file" name="my_file"><br>
<input type="submit" name="" value="upload">
</form>
</body>
</html>
Now here is the NodeJS
const express = require("express");
const app = express();
const fileUpload = require('express-fileupload');
app.use(fileUpload({ safeFileNames: true, preserveExtension: true }))
app.post('/', function(req, res) {
// The name of the input field (i.e. "sampleFile") is used to retrieve the uploaded file
let the_file = req.files.my_file;
the_file.mv('/path/to/html/uploads/up/' + the_file.name , function(err) {
res.writeHead(200, {"Content-Type": "text/plain"});
if(err){
console.log(err);
res.write(err);
res.end();
} else {
console.log("uploaded");
res.write("upload of file "+the_file.name+" complete");
res.end();
}
});
});
module.exports = app;
You have to create folder images!
//install path module
const path = require('path');
// remaining code
let startup_image = req.files.image;
startup_image.mv(path.resolve(__dirname,'/images',startup_image.name), function(error){
//remaining code
})
this way
file.mv(path.resolve(__dirname, '../public/images', filename)

zero byte file is created in s3 while trying to resize and upload using multer in nodejs?

Below is my app.js file.. Whenever i make an attempt to resize and upload my image using multer-imager module a zero byte file is getting created everytime and i am not getting any response (keeps loading on post action).
/*********app.js*********/
var express = require('express'),
aws = require('aws-sdk'),
bodyParser = require('body-parser'),
multer = require('multer'),
imager = require('multer-imager'),
multerS3 = require('multer-s3');
gm = require('gm');
var Upload = require('s3-uploader');
var app = express(),
s3 = new aws.S3();
app.use(bodyParser.json());
var upload = multer({
storage: imager({
dirname: 'directory',
bucket: 'bucket',
accessKeyId: 'accessKeyId',
secretAccessKey: 'my secretAccessKey',
region: 'my region',
filename: function (req, file, cb) { // [Optional]: define filename (default: random)
cb(null, Date.now()) // i.e. with a timestamp
}, //
gm: { // [Optional]: define graphicsmagick options
width: 200, // doc: http://aheckmann.github.io/gm/docs.html#resize
height: 200,
options: '!',
format: 'png' // Default: jpg
},
s3 : { // [Optional]: define s3 options
Metadata: { // http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectPUT.html
'customkey': 'data' // "x-amz-meta-customkey","value":"data"
}
}
})
});
app.post('/upload', upload.any(), function(req, res, next){
console.log(req.files); // Print upload details
res.send('Successfully uploaded!');
});
app.get('/', function (req, res) {
res.sendFile(__dirname + '/index.html');
});
app.listen(3001, function () {
console.log('Example app listening on port 3001!');
});
The below is my index.html file.
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
</head>
<body>
Hey! Lets try uploading to s3 directly :)
<form method="post" enctype="multipart/form-data" action="/upload">
<p>
<input type="text" name="title" placeholder="optional title"/>
</p>
<p>
<input type="file" name="upl"/>
<!-- <input type="file" name="uplo"/> -->
</p>
<p>
<input type="submit"/>
</p>
</form>
</body>
</html>
but i can able up upload an image without doing anykind of modification using multer-s3 module.But resizing is mandatory for me.Help me to get rectify these error.
I think GraphicsMagick package not installed in your system(Not NPM package).
Please go through GraphicsMagick guide and install GraphicsMagick in your system
http://www.graphicsmagick.org/README.html

Resources