File not uploading in Express JS using Multer - javascript

I am creating API using express JS. Now, I have a router which will be used to upload image using multer.
Here is my router :
const multer = require('multer');
module.exports = (app) => {
const DIR = './public/';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, DIR);
},
filename: (req, file, cb) => {
cb(null , file.originalname);
}
});
const upload = multer({ storage: storage });
// I have also tried this but not working
// const upload = multer({ dest: 'uploads/' });
app.post('/upload', upload.single('image'), (req, res, next) => {
res.status(201).json({
message: "File uploaded successfully"
});
});
}
Now, from my reactjs app I am calling this router using axios like this :
const headers = {
"Content-Type": "multipart/form-data"
}
const body = new FormData();
body.append('image', this.state.selectedCategoryImage);
axios.post('http://localhost:3000/upload', body, { headers }).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
In above code this.state.selectedCategoryImage is a selected image from html <input> tag.
Now, When I call this api I am getting my response "file uploaded successfully", but I am not able to see my uploaded image anywhere in public directory. My image is not uploading.
Please anyone can help me what's the issue ?

Pass file Object not URL
URL.createObjectURL(file) // it return file url that you can use to show file preview
For upload file, send actual file in axios API as you got from file input
var file = event.target.files[0]; // return actual file
this way it actually send file object.

Related

Stop multer from uploading when field is empty

I'm trying to make a post request for users to update multiple parts of their profile at once, but if they only want to update their bio and not their profile picture for example, how can I stop multer from trying to upload if the field is left blank?
post request:
app.post('/updateprofile/:user_id', upload.single("profilePic"), function(req, res){
let newPic = req.file.filename
let bio = req.body.bio
...
})
multer storage options:
var storage = multer.diskStorage({
destination: function(req, file, cb) {
cb(null, './public/uploads')
},
filename: function(req, file, cb) {
cb(null, Date.now() + file.originalname)
}
})
When I leave it empty the error I get is
TypeError: Cannot read property 'filename' of undefined
just check if any file was added with if (!req.file) { //Dont do anything since there is no file } else { //do something const fileName = req.file.filename; }

Why is my file not being considered after multer middleware?

I am trying to upload a filename and a post (9 gag style) to my mysql database, and store the file into my project folder.
I am getting the file from the front end (console.log in the front works perfectly), then i Use nodejs + express backend to handle the file and send it to my database.
I created a multer middleware to set the filename, extension, disk location, then in a controller i am trying to get the file to send it do database. The console.log "route ok" is fine, but when i console.log req.file or req.file.filename, or req.body.file, etc... I get an undefined answer...
I really don't see what is wrong with my code, I already used it in another projet i worked fine but i was doing only the backend.
Here is my front end code (vue js) :
<template>
<div id="container">
<div id="formPost">
<button class="btn btn-primary" #click.prevent="displayFormPost = !displayFormPost">Créer un post</button>
<form v-if="displayFormPost">
<input type="text" class="form-control" placeholder="Entrer la description de l'image" v-model="messageToPost">
<input type="file" ref="file"> <button class="btn btn-primary" #click.prevent="postMessage">Poster</button>
</form>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
data() {
return {
displayFormPost: false,
messageToPost: ''
}
},
methods: {
postMessage() {
let file = this.$refs.file.files[0];
let message = this.messageToPost;
console.log(file);
axios.post('http://localhost:3000/wall/post/', { message , file } )
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
})
}
}
}
</script>
My multer-config middleware :
const MIME_TYPES = {
'image/jpg': 'jpg',
'image/jpeg': 'jpg',
'image/png': 'png',
'image/gif': 'gif'
}
const storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, 'images')
},
filename: (req, file, callback) => {
const name = file.originalname.split(' ').join('_');
const extension = MIME_TYPES[files.mimetype];
callback(null, name + Date.now() + '.' + extension);
}
})
module.exports = multer({ storage }).single('image');
and my controller (i have been just trying to display filename etc so far) :
exports.postMessage = (req, res, next) => {
console.log(req.file)
console.log('routeok');
}
And my routes set with multer :
const express = require('express');
const router = express.Router();
const multer = require('../middleware/multer-config');
const wallControllers = require('../controllers/wall');
router.post('/post/', multer, wallControllers.postMessage);
module.exports = router;
Ps : no error in the console at all
Thank you !!
I think there are 2 issues:
First of all, when you set up multer you tell it to expect the file in a field named "image":
multer({ storage }).single('image');
But you never name any field as "image", not in the HTML, nor when you call axios.
Also, to send a file, you must set a proper Content-Type HTTP header and you should use the FormData API, as described in this answer.
So you may try:
const formData = new FormData();
formData.append("image", file);
formData.append("message", message);
axios.post('http://localhost:3000/wall/post/', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
})

set multer destination in api.js in nodejs and call another function?

i am using a api in server.js file.
app.post('/api/uploadalumniresume', api.UploadAlumniResume);
my api.js file code is:
exports.UploadAlumniResume = (req, res) => {
const storage = multer.diskStorage({
destination(req, file, cb) {
// cb(null, `${__dirname}public/images/uploads`);
cb(null, `${__dirname}/../public/imports`);
},
filename(req, file, cb) {
cb(null, `${Date.now()}-${file.originalname}`);
},
});
const importupload = multer({
storage,
});
const importmulter = importupload.single('file');
};
The server.js as call the api but the exports api not saving the uploading file to the destination folder
And i need to store the upload file on destination follder.
And if the file as uploaded then function need to work on create fuction in the same api.
please give a any solution to me!

nodejs multer diskstorage to delete file after saving to disk

I am using multer diskstorage to save a file to disk.
I first save it to the disk and do some operations with the file and then i upload it to remote bucket using another function and lib.
Once the upload is finished, i would like to delete it from the disk.
var storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename: function (req, file, cb) {
cb(null, file.fieldname + '-' + Date.now())
}
})
var upload = multer({ storage: storage }).single('file')
and here is how i use it:
app.post('/api/photo', function (req, res) {
upload(req, res, function (err) {
uploadToRemoteBucket(req.file.path)
.then(data => {
// delete from disk first
res.end("UPLOAD COMPLETED!");
})
})
});
how can i use the diskStorage remove function to remove the files in the temp folder?
https://github.com/expressjs/multer/blob/master/storage/disk.js#L54
update:
I have decided to make it modular and put it in another file:
const fileUpload = function(req, res, cb) {
upload(req, res, function (err) {
uploadToRemoteBucket(req.file.path)
.then(data => {
// delete from disk first
res.end("UPLOAD COMPLETED!");
})
})
}
module.exports = { fileUpload };
You don't need to use multer to delete the file and besides _removeFile is a private function that you should not use.
You'd delete the file as you normally would via fs.unlink. So wherever you have access to req.file, you can do the following:
const fs = require('fs')
const { promisify } = require('util')
const unlinkAsync = promisify(fs.unlink)
// ...
const storage = multer.diskStorage({
destination(req, file, cb) {
cb(null, '/tmp/my-uploads')
},
filename(req, file, cb) {
cb(null, `${file.fieldname}-${Date.now()}`)
}
})
const upload = multer({ storage: storage }).single('file')
app.post('/api/photo', upload, async (req, res) =>{
// You aren't doing anything with data so no need for the return value
await uploadToRemoteBucket(req.file.path)
// Delete the file like normal
await unlinkAsync(req.file.path)
res.end("UPLOAD COMPLETED!")
})
Multer isn't needed. Just use this code.
const fs = require('fs')
const path = './file.txt'
fs.unlink(path, (err) => {
if (err) {
console.error(err)
return
}
//file removed
})
You may also consider using MemoryStorage for this purpose, with this storage the file is never stored in the disk but in memory and is deleted from the memory automatically after execution comes out of controller block, i.e., after you serve the response in most of the cases.
When you will use this storage option, you won't get the fields file.destination, file.path and file.filename, instead you will get a field file.buffer which as name suggests is a buffer, you can convert this buffer to desired format to do operations on and then upload using a stream object.
Most of the popular libraries support streams so you should be able to use stream to upload your file directly, code for converting buffer to stream:
const Readable = require('stream').Readable;
var stream = new Readable();
stream._read = () => { }
stream.push(file.buffer);
stream.push(null);
// now you can pass this stream object to your upload function
This approach would be more efficient as files will be stored in memory which will result in faster access, but it does have a con as mentioned in multer documentation:
WARNING: Uploading very large files, or relatively small files in
large numbers very quickly, can cause your application to run out of
memory when memory storage is used.
To do it truly automatically across all routes I used this strategy :
when the request ends, we delete all the uploaded files (req.files). Before that, if you want to keep the files on the server, you need to save them in another path.
var express = require('express');
var app = express();
var http = require('http');
var server = http.Server(app);
// classic multer instantiation
var multer = require('multer');
var upload = multer({
storage: multer.diskStorage({
destination: function (req, file, cb) {
cb(null, `${__dirname}/web/uploads/tmp/`);
},
filename: function (req, file, cb) {
cb(null, uniqid() + path.extname(file.originalname));
},
}),
});
app.use(upload.any());
// automatically deletes uploaded files when express finishes the request
app.use(function(req, res, next) {
var writeHead = res.writeHead;
var writeHeadbound = writeHead.bind(res);
res.writeHead = function (statusCode, statusMessage, headers) {
if (req.files) {
for (var file of req.files) {
fs.unlink(file.path, function (err) {
if (err) console.error(err);
});
}
}
writeHeadbound(statusCode, statusMessage, headers);
};
next();
});
// route to upload a file
router.post('/profile/edit', access.isLogged(), async function (req, res, next) {
try {
// we copy uploaded files to a custom folder or the middleware will delete them
for (let file of req.files)
if (file.fieldname == 'picture')
await fs.promises.copy(file.path, `${__dirname}/../uploads/user/photo.jpg`);
} catch (err) {
next(err);
}
});
I have removed directory after file uploaded using fs-extra
const fs = require('fs-extra');
// after you uploaded to bucket
await fs.remove('uploads/abc.png'); // remove upload dir when uploaded bucket

How to upload image using javascript fetch api and express multer

I am working in a reactjs application where i have to upload user image. I am getting file on onChange event of file input and passing it parent component and parent component will make a post request using the data
Server side I am using express and multer for file upload and client side using fetch api to upload the image.
Thanks in advance :)
I figure it out
To upload an file/image to multer we need a form enctype="multipart/form-data" without that it wont work with multer
I am getting file from a child component then
1) i have created a empty form with the encType="mutipart/form-data"
2) when i received the file i create a new FormData(with ref to myform)
3) then append key and value in the formData
4) create fetch.post() and it works :)
for ref submitting the code
React Parent component Upload.js
import React, { Component } from 'react'
import { ImageWithoutForm } from "../app/components/ImageUpload";
export default class UploadFile extends Component {
onImageLoad(e){
console.log('onImageLoad', e.target.files[0]);
this.uploadForm(e.target.files[0]);
}
uploadForm(file){
let form = new FormData(this.refs.myForm);
form.append('myImage', file);
fetch('/upload-image', {
method: 'POST',
body: form
}).then(res => console.log('res of fetch', res));
}
render() {
return (
<div>
<h4>Upload Image</h4>
<ImageWithoutForm onImageLoad={(e)=>this.onImageLoad(e)} />
<form id="upload_form" ref="myForm" encType="multipart/form-data">
</form>
</div>
)
}
}
React Child Component with input to load the file ImageWithoutForm.js
import React, { Component } from 'react'
export class ImageWithoutForm extends Component {
handleSubmit(e){
this.props.onImageLoad(e);
}
render() {
return (
<div>
<input type="file" onChange={(e)=>this.handleSubmit(e)}/>
</div>
)
}
}
Express Route file taken from someone github repo and customized UploadImage.js
const express = require('express');
const multer = require('multer');
const path = require('path');
// Set Storage Engine
const storage = multer.diskStorage({
destination: './public/uploads/',
filename: function(req, file, cb){
cb(null,file.fieldname + '-' + Date.now() + path.extname(file.originalname));
}
});
// Init Upload
const upload = multer({
storage: storage,
limits:{fileSize: 1000000},
fileFilter: function(req, file, cb){
checkFileType(file, cb);
}
}).single('myImage');
// Check File Type
function checkFileType(file, cb){
// Allowed ext
const filetypes = /jpeg|jpg|png|gif/;
// Check ext
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
// Check mime
const mimetype = filetypes.test(file.mimetype);
if(mimetype && extname){
return cb(null,true);
} else {
cb('Error: Images Only!');
}
}
// Init app
const app = express.Router();
// Public Folder
app.use(express.static('./public'));
app.post('/', (req, res) => {
console.log('handling upload image');
upload(req, res, (err) => {
if(err){
console.log('first err', err);
res.send({
msg: err
});
} else {
if(req.file == undefined){
console.log('Error: No File Selected!')
res.send({
msg: 'Error: No File Selected!'
});
} else {
console.log('File Uploaded!')
res.send({
msg: 'File Uploaded!',
file: `uploads/${req.file.filename}`
});
}
}
});
});
module.exports = app;
and in my express app.js just require the route file ImageUpload.js
and map to the route like this
var uploadImage = require('./routes/UploadImage');
server.use('/upload-image', uploadImage);

Categories