I am making a small script to upload a photo and save it to local storage. I'm using Express and Muller for this.
I have an html page with an upload field (see below). As soon as I press the 'upload photo' button he sends the photo to the endpoint '/upload'. Here he should save the photo on my computer under the name 'image.jpg' only when it comes in the post the 'req.file' is empty.
I just wonder what is going wrong? Am I missing something?
App.js
const express = require("express");
const multer = require("multer");
const helpers = require("./helpers");
const app = express();
const port = process.env.PORT || 3000;
app.use(express.static(__dirname + "/"));
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, "uploads/");
},
filename: function (req, file, cb) {
cb(null, "image.jpg");
},
});
app.listen(port, () => console.log(`Listening on port ${port}...`));
app.post("/upload", (req, res) => {
let upload = multer({
storage: storage,
fileFilter: helpers.imageFilter,
}).single("image");
upload(req, res, function (err) {
if (req.fileValidationError) {
return res.send(req.fileValidationError);
} else if (!req.file) {
return res.send("Please select an image to upload");
} else if (err instanceof multer.MulterError) {
return res.send(err);
} else if (err) {
return res.send(err);
}
res.send(
`You have uploaded this image: <hr/><img src="${req.file.path}" width="500"><hr />Upload another image`
);
});
});
Helpers.js
const imageFilter = function(req, file, cb) {
// Accept images only
if (!file.originalname.match(/\.(jpg|JPG|jpeg|JPEG|png|PNG|gif|GIF)$/)) {
req.fileValidationError = 'Only image files are allowed!';
return cb(new Error('Only image files are allowed!'), false);
}
cb(null, true);
};
exports.imageFilter = imageFilter;
HTML Page
<form action="/upload" enctype="multipart/form-data" method="POST">
<input type="file" name="image" accept="image/*" />
<input type="submit" value="Upload Photo" />
</form>
It went wrong at the 'const storage'. I filled in the wrong file path. But it strange that the req.file was empty ... Anyway, it works!
Related
I am build a web app with Node.js and React and I am trying to store some files at my backend.
For some reason i cant access to my req.path , although I configured all multer strategies.
const multer = require('multer')
const config = require('config')
const auth = require('../../middleware/auth')
const {check , validationResult } = require('express-validator');
const storage = multer.diskStorage({
destination: function(req, file , cb){
cb(null,'uploads/')
},
filename: function(req, file, cb){
cb(null, req.user.id)
}
});
const fileFilter = (req, file , cb) =>{
if(file.mimetype === 'image/jpeg' || file.mimetype === 'image/jpg' || file.mimetype === 'image/png')
cb(null,false);
else
cb(null,true);
}
my route:
router.post('/' , [auth,upload.single('image'),
[
check('status','Status is required').not().isEmpty(),
check('skills','Skills is required').not().isEmpty(),
check('gender','Gender is required').not().isEmpty()
]],
async (req,res) => {// cant access to req.file.....}
and my react form:
<div className="form-group">
<input type="text" placeholder="Choose profile image" name="profileImage" value={image}/>
<input type="file" placeholder="Upload" enctype="multipart/form-data" name="image" onChange={(e) => onChange(e)}/>
<small className="form-text">The image must not be bigger then 5MB and only JPG\JPEG\PNG types</small>
</div>
Please , what am i doing wrong?
I have used multer in my backend application and this is how I have configured it and it works properly and stores required files in server file directory.
First, installing multer
npm i multer --save
Second, initialize it at the top of required .js file
const multer = require("multer");
Now, configuring storage (storage location and filename)
const storage = multer.diskStorage({
destination: "./public/uploads/postimages/",
filename: function (req, file, cb) {
cb(
null,
file.fieldname + "-" + Date.now() + path.extname(file.originalname)
);
},
});
Init multer function, (configure own file size)
Use array if you want to upload multiple files at once, 10 is the number of files, you can modify it as needed.
const upload = multer({
storage: storage,
limits: { fileSize: 10000000000 },
}).array("image", 10);
// Use .single("image"); if you want to upload a single file.
// image is the name/key that is sent from frontend with the file(s).
If you are using array, you can create an api that stores file, which will look like this.
try {
let imagePath = "abc";
let postId = req.params.postId;
let imagesLinks = [];
await upload(req, res, (err) => {
if (err) {
console.log(err);
} else {
if (req.files == undefined) {
console.log("File not found.");
} else {
//image uploaded successfully
req.files.map(function (file) {
imagePath = "uploads/postimages/" + file.filename;
imagesLinks.push(tmp);
});
}
}
res.status(201).send(imagesLinks);
});
}
For a single file, it looks as simple as this
try {
let imagePath = "abc";
upload(req, res, (err) => {
if (err) {
res.status(300).send(err);
console.log(err);
} else {
if (req.file == undefined) {
res.status(301).send("image upload failed.");
} else {
//image uploaded successfully
imagePath = "uploads/" + req.file.filename;
storeImageLink(res, imagePath);
}
}
});
}
Look at these examples. They will be help you:
File Upload With Multer in Node.js and Express
Multer Storage:
// 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())
}
})
var upload = multer({ storage: storage })
Handling File Uploads:
app.post('/uploadfile', upload.single('myFile'), (req, res, next) => {
const file = req.file
if (!file) {
const error = new Error('Please upload a file')
error.httpStatusCode = 400
return next(error)
}
res.send(file)
})
Reactjs nodejs upload image — How to upload image using reactjs and nodejs (multer)
Node.js:
const path = require("path");
const multer = require("multer");
const storage = multer.diskStorage({
destination: "./public/uploads/",
filename: function(req, file, cb){
cb(null,"IMAGE-" + Date.now() + path.extname(file.originalname));
}
});
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
}).single("myImage");
const router = express.Router();
router.post("/upload", {
upload(req, res, (err) => {
console.log("Request ---", req.body);
console.log("Request file ---", req.file);//Here you get file.
/*Now do where ever you want to do*/
if(!err)
return res.send(200).end();
});
};);
Multer is able to upload my file, but only if I use the dest option. When I use diskStorage, it doesn't work. I'm sure I'm doing it wrong, so feel free to correct me. Here is my code...
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "/uploads");
},
filename: (req, file, cb) => {
cb(null, Date.now() + "-" + file.originalname);
}
})
const upload = multer({ storage: storage });
router.post('/signup', upload.single("avatar"), (req, res, next) => {
console.log(req.file);
const { heroname, pass, pass2 } = req.body;
const salt = bcrypt.genSaltSync(12);
const hashedPassWord = bcrypt.hashSync(pass, salt);
User.create({
name: heroname,
password: hashedPassWord
})
.then(() => {
console.log('Task complete!');
res.redirect('/')
})
.catch((err) => {
next(err);
})
});
And here is the associated HTML...
<div class="form-item">
<label for="avatar">Choose your Avatar:</label>
<input id="avatar" name="avatar" type="file" accept="image/*">
</div>
When I log the req.file to the console, it shows the correct information. When I look inside the uploads folder, nothing uploads.
It turns out I needed a ./ for my uploads folder file path, not a /.
So I figured out how to upload files using React and Node. It appears to be working, but I'm still very new to this stuff, and I can't quite understand how to access the file I just uploaded in React. I want to have it so that you use a form in React to upload a zip file, and then I want to have some scripts unzip the uploaded file and perform some actions on the contents. I have my files uploading properly, but I'm not sure how to pass the filename data into React after upload..
My server file:
const port = process.env.PORT || 5000;
const express = require('express');
const bodyParser = require('body-parser');
const multer = require('multer');
const uuidv4 = require('uuid/v4');
const path = require('path');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './src/uploads');
},
filename: (req, file, cb) => {
const newFilename = `${uuidv4()}${path.extname(file.originalname)}`;
cb(null, newFilename);
},
});
var fileFilter = function (req, file, cb) {
if (
file.mimetype !== 'application/zip'
) {
req.fileValidationError = 'goes wrong on the mimetype';
return cb(new Error('mimetype does not match application/zip. upload rejected'));
}
console.log('>> fileFilter good = ',file.mimetype)
cb(null, true);
}
const upload = multer({ storage: storage, fileFilter: fileFilter });
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.post('/', upload.single('selectedFile'), (req, res) => {
res.send();
});
app.listen(port, () => console.log(`Server listening on port ${port}`));
My React File:
import React, { Component } from 'react';
import axios from 'axios';
class UserForm extends Component {
constructor() {
super();
this.state = {
description: '',
selectedFile: '',
};
}
onChange = (e) => {
switch (e.target.name) {
case 'selectedFile':
this.setState({ selectedFile: e.target.files[0] });
break;
default:
this.setState({ [e.target.name]: e.target.value });
}
}
onSubmit = (e) => {
e.preventDefault();
const { description, selectedFile } = this.state;
let formData = new FormData();
formData.append('description', description);
formData.append('selectedFile', selectedFile);
console.log('form data ',formData)
axios.post('/', formData)
.then((result) => {
console.log('>> (onSubmit) file upload result = ',result);
// access results...
})
.catch(function (error) {
console.log('>> ERROR FILE UPLAOD ',error);
alert('File upload failed. Please ensure you are uploading a .zip file only')
})
}
render() {
const { description, selectedFile } = this.state;
return (
<form onSubmit={this.onSubmit}>
<input
type="text"
name="description"
value={description}
onChange={this.onChange}
/>
<input
type="file"
name="selectedFile"
onChange={this.onChange}
/>
<button type="submit">Submit</button>
</form>
);
}
}
export default UserForm;
In your case you are uploading a single file. So you need to return it from your / route like this
app.post('/', upload.single('selectedFile'), (req, res) => {
res.send( req.file );
});
When you use Multer and upload a file like this, then req.file will be your uploaded file here, which is selectedFile. So, you need to return it to use where ever you want.
This req.file has some information like originalname, filename, path and so on. You can use those information on your front-end. For example, you can grab path (which is the full path of the uploaded file) then use it in an <img> element.
Specifically for your situation, you can hold an imagePath state:
this.state = {
description: '',
selectedFile: '',
imagePath: '',
};
Then update your state within your axois' .then method:
axios.post('/', formData)
.then((result) => {
this.setState({imagePath: result.data.path})
})
....
and use it in your component:
{this.state.imagePath && <img src={this.state.imagePath} /> }
This is a very simple example, logic can be more complex when your app gets bigger.
I'm trying to save the image file using multer package.
Here is our registration.js which contains the registration form. The following scripts have no errors, but we don't know how to access the file in url or show the image to users (exactly how to save it with its MIME format).
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
export default class Registration extends Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleSubmit(e) {
e.preventDefault();
var config = {
headers: {
'Content-Type': 'multipart/form-data'
}
};
var formData = new FormData();
var imagefile = document.getElementById('profilePicture');
formData.append('name',document.getElementById('name').value);
formData.append('email',document.getElementById('email').value);
formData.append('telegramId',document.getElementById('telegramId').value);
formData.append('password',document.getElementById('password').value);
formData.append('image', imagefile.files[0]);
console.log(formData);
axios.post('/registration', formData,config).then(function(res){
console.log(res.data+'res');
}).catch(console.error);
}
render() {
return (<form className="registrationForm">
<input type="text" name="name" id="name" required="required" placeholder="name" />
<br/>
<input type="email" id="email" required="required" placeholder="email"/>
<br/>
<input type="text" id="telegramId" required="required" placeholder="telegramID"/>
<br/>
<input type="password" id="password" required="required" placeholder="password"/>
<br/>
<input type="file" id="profilePicture"/>
<br/>
<button className="registerButton" onClick={this.handleSubmit}>Register</button>
</form>)
};
}
and this is server side code:
var multer = require('multer')
var upload = multer({ dest: '../assets/uploads/' })
app.post('/registration',upload.single('image'), (req, res) => {
// console.log(req.body);
console.log(req.file);
});
and this is what the console has logged!
{ fieldname: 'image',
originalname: 'sharif-logo-png-transparent.png',
encoding: '7bit',
mimetype: 'image/png',
destination: 'uploads/',
filename: 'e098a8370f444f321acd9aae668538fd',
path: 'uploads\\e098a8370f444f321acd9aae668538fd',
size: 271654
}
multer already takes care of saving the file to disk, so all you need to do is make that folder accessible to be used in <img> tags (or whatever you'll want to use to display them). A good solution would be express.static:
const { static } = require('express');
app.use('/images/', static('../assets/uploads/'));
Then you can use them in your client-side code:
<img src="/images/e098a8370f444f321acd9aae668538fd"></img>
You don't have to handle the file at all in the .post('/registration') handler.
const express = require('express');
const multer = require('multer');
const path = require('path');
const router = express.Router()
// Image Upload
const imageStorage = multer.diskStorage({
destination: 'images', // Destination to store image
filename: (req, file, cb) => {
cb(null, file.fieldname + '_' + Date.now() + path.extname(file.originalname))
// file.fieldname is name of the field (image), path.extname get the uploaded file extension
}
});
const imageUpload = multer({
storage: imageStorage,
limits: {
fileSize: 1000000 // 1000000 Bytes = 1 MB
},
fileFilter(req, file, cb) {
if (!file.originalname.match(/\.(png|jpg)$/)) { // upload only png and jpg format
return cb(new Error('Please upload a Image'))
}
cb(undefined, true)
}
})
// For Single image upload
router.post('/uploadImage', imageUpload.single('image'), (req, res) => {
res.send(req.file)
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
// For Multiple image upload
router.post('/uploadBulkImage', imageUpload.array('images', 4), (req, res) => {
res.send(req.files)
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
// ---------------------------------------------------------------------------- //
// Video Upload
const videoStorage = multer.diskStorage({
destination: 'videos', // Destination to store video
filename: (req, file, cb) => {
cb(null, file.fieldname + '_' + Date.now() + path.extname(file.originalname))
}
});
const videoUpload = multer({
storage: videoStorage,
limits: {
fileSize: 10000000 // 10000000 Bytes = 10 MB
},
fileFilter(req, file, cb) {
if (!file.originalname.match(/\.(mp4|MPEG-4)$/)) { // upload only mp4 and mkv format
return cb(new Error('Please upload a Video'))
}
cb(undefined, true)
}
})
router.post('/uploadVideo', videoUpload.single('video'), (req, res) => {
res.send(req.file)
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
module.exports = router
It simply doesn't save anything to the destination folder i specified.
i tried {storage:storage} instead of {dest: 'storage/'} but it didn't work either.
the image data is actually sent to the server as its console logged. and the dest i specified is created by default but remain empty.
const express = require('express');
const app = express();
const multer = require('multer');
let storage = multer.diskStorage({
destination: '/public/my-uploads',
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
const upload = multer({dest:'storage/'}).single('file');
app.post('/upload', upload, (req , res) => {
console.log(req.files) // this does log the uploaded image data.
})
***** EDIT ******
HTML
<form onSubmit={this.upload} enctype='multipart/form-data'>
<input type='file' name='image' />
<input type='submit' value='upload' />
</form>
JS
upload(e){
e.preventDefault();
const file = e.target[0].files[0];
console.log(file)
const fm = new FormData();
fm.append('file', file);
console.log(fm)
axios.post('/upload', fm);
}
POSTMAN
Try to catch the error my calling the middleware yourself:
var upload = multer().single('avatar')
app.post('/upload', function (req, res) {
upload(req, res, function (err) {
if (err) {
// An error occurred when uploading
return
}
// Everything went fine
})
})
Also, change the storage to this:
let storage = multer.diskStorage({
destination: function(req, file, ca) {
cb(null, '/public/my-uploads');
}
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
It's been a while, the issue was that I wasn't using the multer middleware at all so the callback code for handling the image was never executed.
I didn't know much about how express worked back then.
Seems you are not using the storage variable and use a function for the destination key, as written in the documentation, also you need to pass your file in the input field named field otherwise multer can't store the file, create an storage folder on the same level as the code :
const http = require('http')
const port = 3000
const express = require('express');
const app = express();
const multer = require('multer');
const server = http.createServer(app)
let storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, './storage')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
});
const upload = multer({ storage }).single('file');
app.post('/upload', upload, (req, res) => {
console.log(req.files) // this does log the uploaded image data.
})
// bind the server on port
server.listen(port, (err) => {
if (err) {
return console.log('something bad happened', err)
}
console.log(`server is listening on ${port}`)
})
The name of the "single" in ...single('file') must match the name in the input (containing the file) <input type="file" name='image' /> - and it does not in your example.
Change the multer-part to this ...single('image') - as in the input name='image'
Try this File Storage For Save image in Local
const fileStorage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "images");
},
filename: (req, file, cb) => {
cb(
null,
new Date().toISOString().replace(/:/g, "-") + "-" + file.originalname
);
},
});