How to decode binary image data retrieved from mongodb in nodejs - node.js

I am trying to upload and retrieve image to and from mongodb through nodejs and mutter. But i am stuck some where, i hope i am succeeded in uploading image as binary data. but not displaying image in my .ejs file.
Routes file
const express=require('express');
const adminRouter=express.Router();
const Bookdata=require('../model/Bookdata')
const multer = require('multer');
const path = require('path');
var fs = require('fs');
const {
GridFsStorage
} = require("multer-gridfs-storage");
require("dotenv")
.config();
// set up multer for storing uploaded files
const storage=multer.diskStorage({
//destination for files
destination:function(request,file,callback){
callback(null,'../LibraryApps/public/uploads/images');
},
//add back the extensions
filename:function(request,file, callback){
callback(null,file.fieldname+Date.now()+path.extname(file.originalname));
}
})
//upload parameters for mutter
const upload = multer({
storage: storage,
limits:{
fileSize: 1000000
},
fileFilter:function(req,file,callback){
checkFileType(file, callback);
}
});
//Check file type
function checkFileType(file, callback){
// allowed extension
const filetypes = /jpeg|jpg|png|gif/;
//check extension
const extname=filetypes.test(path.extname(file.originalname).toLowerCase());
//check mime
const mimetype=filetypes.test(file.mimetype);
if(mimetype&&extname){
return callback(null, true);
}else{
callback('Error: Images only');
}
}
var imgModel = require('../model/Bookdata');
function router(nav){
adminRouter.get('/',function(req,res){
imgModel.find({}, (err, items) => {
if (err) {
console.log(err);
res.status(500).send('An error occurred', err);
}
else {
//res.render('imagesPage', { items: items });
res.render('addBook',{
nav,
title:'Library'
})
}
});
})
adminRouter.post('/add',upload.single(`image`), function(req,res){
// res.send("Hey I am Added");
console.log(req.file);
var item={
title: req.body.title,
author: req.body.author,
genre: req.body.genre,
//image: req.file.image,
image: {
data: fs.readFileSync(path.join('../LibraryApps/public/uploads/images/' + req.file.filename)),
contentType: 'image/png'
}
}
imgModel.create(item, (err, item) => {
if (err) {
console.log(err);
}
else {
var book=Bookdata(item);
book.save();
res.redirect('/books');
}
});
});
return adminRouter;
}
module.exports=router;
my model file
//Accessing Mongose package
const mongoose=require('mongoose');
//Database connection
// mongoose.connect('mongodb://localhost:27017/library');
mongoose.connect('mongodb....');
//Schema definition
const Schema= mongoose.Schema;
const BookSchema=new Schema({
title: String,
author: String,
genre: String,
// image: String,
image:{
data: Buffer,
contentType: String
}
});
//Model creation
var Bookdata= mongoose.model('bookdata',BookSchema);
module.exports=Bookdata;
and this is my .ejs file
<%for(i=0;i<books.length;i++){%>
<div class="row">
<br>
<div class="col-md-2 col-sm-3 text-center">
<a class="story-title" href="#">
<img src="data:<%=books[i].image.contentType%>;base64,{Buffer.from('<%=books[i].image.data%>','binary').toString('base64')}" style="width:100px;height:100px" class="img-circle">
</a>
</div>
my get function
booksRouter.get('/',function(req,res){
Bookdata.find()
.then(function(books){
res.render("books",{
nav,
title:"Library App",
books
});
})
});
in mongodb, i am getting like this
> _id:6226e5c60e92bdee82dc574a title:"cc" author:"aa" genre:"life" image:Object
> data:Binary('iVBORw0KGgoAAAANSUhEUgAAA+gAAAOECAYAAAAylRvFAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAgY0hSTQAAeiYA...',
> 0) contentType:"image/png"
> __v:0
In Console i am getting error
GET data:image/png;base64,{Buffer.from('%EF%BF%BDPNG%0A%1A%0A%EF%BF%BD%EF%BF%BD%EF%BF%BD%0AIHDR%EF%BF%BD%EF%BF%BD%03%EF%BF%BD%EF%BF%BD%EF%BF%BD%03%EF%BF%BD%08%06%EF%BF%BD%EF%BF%BD%EF%BF%BD2%EF%BF%BD%1B%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%04gAMA%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%0B%EF%BF%BDa%05%EF%BF%BD%EF%BF%BD%EF%BF%BD%01sRGB%EF%BF%BD%EF%BF%BD%EF%BF%BD%1C%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD cHRM%EF%BF%BD%EF%BF%BDz&%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDu0%EF%BF%BD%EF%BF%BD%EF%BF%BD`%EF%BF%BD%EF%BF%BD:%EF%BF%BD%EF%BF%BD%EF%BF%BD%17p%EF%BF%BD%EF%BF%BDQ<%EF%BF%BD%EF%BF%BD%EF%BF%BD%06bKGD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%09pHYs%EF%BF%BD%EF%BF%BD%0E%EF%BF%BD%EF%BF%BD%EF%BF%BD%0E%EF%BF%BD%01%EF%BF%BD+%0E%1B%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDIDATx%EF%BF%BD%EF%BF%BD%EF%BF%BDW%EF%BF%BD$%D7%95%EF%BF%BD%09%EF%BF%BDk%EF%BF%BDp%1D%1EZ%EF%BF%BD%EF%BF%BDH(%EF%BF%BD*%16g%EF%BF%BD%EF%BF%BD%EF%BF%BDNWw%EF%BF%BD%EF%BF%BDy%EF%BF%BDb.%EF%BF%BDb~%EF%BF%BD%EF%BF%BDI%EF%BF%BDb.%EF%BF%BD3]%EF%BF%BDd%15%EF%BF%BD$HB%%EF%BF%BDD%EF%BF%BD%EF%BF%BD%EF%BF%BD%D2%B5%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDEDF&%EF%BF%BDV%01H%10%EF%BF%BD%EF%BF%BD%13%EF%BF%BD%08ws%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDk%EF%BF%BDo%EF%BF%BD%EF%BF%BD*%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx<%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%EF%BF%BDW%EF%BF%BD?%05%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD|%EF%BF%BDx%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%1B%EF%BF%BD%17%EF%BF%BD%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%01x%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%1B%EF%BF%BD%17%EF%BF%BD%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%01x%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%1B%EF%BF%BD%17%EF%BF%BD%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%01x%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%1B%EF%BF%BD%17%EF%BF%BD%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%01%EF%BF%BD%EF%BF%BD%14%EF%BF%BD0%EF%BF%BD%EF%BF%BD%7F%02%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%0B%EF%BF%BD0%EF%BF%BDY\5%7F_D%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%EF%BF%BD%EF%BF%BD%EF%BF%BD%D1%B1%12%EF%BF%BD%EF%BF%BD%EF%BF%BDP%D0%B1%10%EF%BF%BD%19%11-S%EF%BF%BD-%EF%BF%BD%12%EF%BF%BD%EF%BF%BD%EF%BF%BD%13E%EF%BF%BDg%EF%BF%BD%EF%BF%BDYa%3E^%EF%BF%BD%EF%BF%BD%C5%BA%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD|[%EF%BF%BD%08%EF%BF%BD%0F%EF%BF%BD%EF%BF%BD%08%EF%BF%BD%EF%BF%BD%08W%EF%BF%BD%0Bt=%EF%BF%BD%EF%BF%BDL%EF%BF%BD%EF%BF%BD%CC%AEjf%EF%BF%BD%EF%BF%BDB%EF%BF%BD%0Bs%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%11%EF%BF%BD%1F,%EF%BF%BD*(%02%EF%BF%BDKx&%EF%BF%BD.Lb%EF%BF%BD%0A%EF%BF%BD+%DE%90|A%EF%BF%BD%EF%BF%BD%EF%BF%BD%22/e%C3%8F%EF%BF%BD%EF%BF%BD%17%EF%BF%BD%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BDx%3C%1E%EF%BF%BD%EF%BF%BD%EF%BF%BD]%EF%BF%BD net::ERR_INVALID_URL
my mongodb

in the template, just add the properties:
edit: Buffer.from is not needed, as it's already a buffer:
with for loop:
<%for(i=0;i<books.length;i++){%>
<div class="row">
<br>
<div class="col-md-2 col-sm-3 text-center">
<a class="story-title" href="#">
<img src="data:<%=books[i].image.contentType%>;base64,<%=books[i].image.data.toString('base64')%>" style="width:100px;height:100px" class="img-circle">
</a>
</div>
<% } %>

Related

Why does my DataForm not return as an object that my URL.createObjectURL is supposed to read?

All I am trying to accomplish is to give my users the option to upload images. I decided to use mongoDB as my database which means I must store photos locally and then send them to the DB. As far as I know, I am new. The object created by ImageName and ImageData isn't being passed properly to my axios post request
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
Here is my route
const Image = require('../models/imageModel');
const ImageRouter = express.Router();
const multer = require('multer');
const storage = multer.diskStorage({
destination: function (req, file, cb){
cb(null, './uploads/')
},
filename: function(req,file,cb){
cb(null, Date.now() + file.originalname);
}
});
const fileFilter = (req, file, cb) =>{
if(file.mimetype === 'image/jpeg' || file.mimetype ==='image/png'){
cb(null, true);
} else{
//rejects storing file
cb(null, false);
}
};
const upload = multer({
storage: storage,
limits:{
fileSize:1024 *1024 * 5
},
fileFilter: fileFilter
});
// stores image in uploads folder using multers and creates a reference to the file
ImageRouter.route("/upload")
.post(upload.single('imageData'), (req, res, next) => {
console.log(req.body);
const newImage = new Image({
imageName: req.body.imageName,
imageData: req.file.path
});
newImage.save()
.then((result)=>{
console.log(result)
res.status(200).json({
success: true,
document: result
});
})
.catch((err)=> next(err))
});
module.exports = ImageRouter;
here is my model
const Schema = mongoose.Schema;
const ImageSchema = new Schema({
imageName:{
type: String,
default: "none",
required: true
},
imageData: {
type :String,
required: false
}
});
const Image = mongoose.model('Image' , ImageSchema)
module.exports = Image;
Here is my ImageUploader page that calls the function
import axios from 'axios';
import DefaultImg from '../assets/default-img.jpg';
import 'bootstrap/dist/css/bootstrap.min.css';
export default class ImageUploader extends Component {
constructor(props) {
super(props)
this.state = {
multerImage: DefaultImg
}
};
setDefaultImage = (uploadType) => {
if (uploadType === "multer") {
this.setState({multerImage: DefaultImg});
};
};
// function to upload image once it has been captured include multer and
// firebase methods
uploadImage(e, method) {
let imageObj = {};
if (method === "multer") {
let imageFormObj = new FormData();
imageFormObj.append("imageName", "multer-image-" + Date.now());
imageFormObj.append("imageData", e.target.files[0]);
console.log(imageFormObj)
// stores a readable instance of the image being uploaded using multer
this.setState({
multerImage: URL.createObjectURL(e.target.files[0])
});
axios
.post(`http://localhost:5000/api/image/uploadmulter/`, imageFormObj)
.then((data) => {
if (data.data.success) {
alert("Image has been successfully uploaded using multer");
this.setDefaultImage("multerImage");
}
})
.catch((err) => {
alert("Error while uploading image using multer");
this.setDefaultImage("multer");
});
}
};
render() {
return (
<div className="main-container">
<h3 className="main-heading">Image Upload App</h3>
<div className="image-container">
<div className="process">
<h4 className="process__heading">Process: Using Multer</h4>
<p className="process__details">Upload image to a node server, connected to a
MongoDB database, with the help of multer</p>
<input
type="file"
display="block"
className="process__upload-btn"
placeholder="Username"
onChange={(e) => this.uploadImage(e, "multer")
}/>
<img
src={this.state.multerImage}
alt="upload-image"
className="process__image"/>
</div>
</div>
</div>
);
};
};
and finally this is where it's being rendered
// import {EditProfile} from './EditProfile'
import DisplayCats from '../cats/DisplayCats'
// import Button from '#material-ui/core/Button';
import Axios from 'axios';
import ImageUploader from '../ImageUploader';
function ProfilePage(props) {
console.log(props.userInfo)
const user = props.userInfo
console.log(user)
console.log(user._id)
console.log(props.userInfo._id)
let [responseData,
setResponseData] = useState('');
// getLocation = () => {
// navigator
// .geolocation
// .getCurrentPosition(function (position) {
// console.log(position)
// });
// }
const clickHandler = (e) => {
this
.props
.history
.push('/DisplayCats')
}
// const setProfileImage = (event) => {
// Axios
// .post('http://localhost:5000/api/users/updateImage/' + user._id, {
// "_id": user._id,
// "profileImage": event.target.value
// })
// .then(res => {
// setResponseData(res.data)
// console.log(responseData)
// }, function (err) {
// console.log(err)
// })
// }
return (
<div style={{
color: "black"
}}>
<h5>This is {props.userInfo.firstName}'s Profile Page</h5>
<h5>Last name: {props.userInfo.lastName}</h5>
<h5>Age: {props.userInfo.age}</h5>
<h5>Location:{props.userInfo.location}</h5>
<h5>Image:{props.userInfo.profileImage}</h5>
<h5>Biography:{props.userInfo.biography}</h5>
<ImageUploader user={user}/>
{/* <div className="col-md-6 img">
<img
src={responseData.profileImage}
alt="profile image"
className="img-rounded"/>
</div> */}
<div className="row">
<DisplayCats user={user}/>
</div>
{/*
<Button
variant="outlined"
color="primary"
onClick={this.clickHandler}
component={this.EditProfile}
user={this.props.user}>
Edit Info
</Button> */}
</div>
)
}
export default ProfilePage;
I want my image data and and image name to be created into a URL for my other functions to read it. My error comes back as POST /api/image/uploadmulter/ 404 97.290 ms - 163
Perhaps im missing something but from the code you shared, you set your route as:
ImageRouter.route("/upload")
so, your client side code should be posting to: http://localhost:5000/upload
yet, your code does this:
.post(`http://localhost:5000/api/image/uploadmulter/`
You're getting a 404 error, which makes sense since this route hasn't been defined.
On a related note, I'd recommend using something like react-uploady, to manage the uploads on your client-side. It will save you a lot of code and bugs. Especially if you want to show preview or other related functionality (like: progress, cancel, retry, etc.).

Image file not being uploaded/created using multer

I am building a blog site, everything is working fine except the image upload on my Post.
The post works 100% but when i attach image it does not get uploaded or created, i don't know what i am doing wrong, please i need a help. I started learning node and express few months ago. Please help me.
inside index.js file
var multer = require('multer');
var upload = multer({dest: './uploads'});
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
My schema
const BlogPost = new Schema({
author: ObjectId,
title: String,
body: String,
profileimage: String,
date: { type: Date, default: Date.now },
comments: [{ type: Schema.Types.ObjectId, ref: 'Comment' }]
});
var Post = mongoose.model('Post', BlogPost);
router.post("/makepost", upload.single('profileimage'), (req, res) => {
var myData = new Post(req.body);
if(req.file) {
console.log('Uploading File....');
var profileimage = req.file.filename;
console.log(profileimage)
}else {
console.log('No File Uploaded....');
var profileimage = 'noimage.jpg';
}
myData.save()
.then(item => {
res.redirect('/posts');
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});
and here is my view
<form action='/makepost' method='post' enctype="multipart/form-data">
<p>Title: <input type='text' name='title' </p>
<p>Content: <input type='text' name='body' </p>
<div class="form-group"> <label>Profile Image</label><input class="form-control"
name="profileimage" type="file" />
<p><input type='submit' value='post'></p>
Since you're using multer with disk-storage, you need to read the image from the temporary location before storing it with mongoose. Besides I would change the image datatype in the mongoose schema to buffer.
Something like this should work (note that I'm using async/await instead of promises directly):
router.post("/makepost", upload.single('profileimage'), async (req, res) => {
const newPost = new Post(req.body);
if (req.file) {
const imgBuffer = await fs.promises.readFile(req.file.path); // TODO add error handling
newPost.profileimage = imgBuffer;
}
try {
await myData.save();
res.redirect('/posts');
}catch(err) {
res.status(400).send("unable to save to database");
}
});
// mongoose schema
...
profileimage: Buffer,
...
You could also store the image as a Base64-encoded string and keep the datatype string in the schema.
EDIT:
As discussed in the comments - change the datatype of profileimage back to string as profileimage will just contain the path to the file.
router.post("/makepost", upload.single('profileimage'), async (req, res) => {
const newPost = new Post(req.body);
if (req.file) {
newPost.profileimage = req.file.path;
}
...
Add an express middleware to serve the images, e.g.
app.use(express.static('./uploads'));

Trying to send formData with another parameter via axios

I have a form that uploads single files via a FormData object. In addition to the file attribute, I want to also assign this file to a Group.
I can't figure out how to add the group object id to the request.
Here is my upload component:
<template>
<div>
<div class="ui attached segment rounded mb-3">
<form #submit.prevent="sendFile" enctype="multipart/form-data" class="ui form">
<div v-if="message" class="`message ${error ? 'text-danger' : 'text-success'}`">
<div class="message-body"> {{ message }}</div>
</div>
<div class="field">
<label for="file" class="label">Upload File</label>
<input
type="file"
ref="file"
#change="selectFile"
class="file-input"
/>
<!-- <span class="file-cta">
<span class="file-icon">
<i class="fas fa-upload"></i>
</span>
<span class="file-label">
Choose a file...
</span>
</span> -->
<span v-if="file" class="file-name">{{file.name}}</span>
<div class="field">
<button class="btn btn-info">Send</button>
</div>
</div>
</form>
</div>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: "SimpleUpload",
data() {
return {
file: "",
message: '',
error: false
}
},
computed: {
currentGroup() {
return this.$store.getters.currentGroup;
}
},
methods: {
selectFile() {
const file = this.$refs.file.files[0];
const allowedTypes = ["image/jpeg", "image/png", "image/gif"];
const MAX_SIZE = 200000;
const tooLarge = file.size > MAX_SIZE;
if (allowedTypes.includes(file.type) && !tooLarge) {
this.file = file;
this.error = false;
this.message = '';
} else {
this.error = true;
this.message = tooLarge ? `Too large. Max size is ${MAX_SIZE/1000}kb` : "Only images are allowed"
}
},
async sendFile() {
const formData = new FormData();
formData.append('file', this.file);
formData.append('group', this.currentGroup);
console.log('file is: ', this.file)
console.log('this is form data after file is added: ', formData)
try {
await axios.post('/uploads', {
formData,
group_id: this.currentGroup.id
});
console.log('this is the formData being sent: ', formData)
this.message = 'File has been uploaded';
this.file = '';
this.error = false;
} catch(err) {
this.message = err.response.data.error;
this.error = true
}
}
}
}
</script>
Here is my route in :
const Group = require('../models/Group');
const Upload = require('../models/Upload');
const router = express.Router();
const upload = require('../services/file-uploads');
const singleUpload = upload.single('file'); // has to match front end
router.post('/', auth.required, async function(req, res) {
console.log ('upload route is hit')
let results = await singleUpload(req, res, async function (err) {
if (err) {
return res.status(422).send({ errors: [{title: 'File upload error, detail: err.message'}] });
} else {
console.log('this is the upload reqest fobject: ', req);
console.log("trying to save data to postgres");
// console.log('this is the originalname of the reqest file object: ', req.file);
console.log('this is the upload reqest body: ', req.body.group_id);
await Upload.query()
.insert({
name: req.body.file.originalname,
users_id: req.user.id,
filetype: req.body.file.mimetype,
filesize: req.body.file.size,
file_url: req.body.file.location,
groups_id: req.body.group_id
})
res.json({
success: true,
message: 'ok'
});
res.json(results)
}
})
});
module.exports = router;
and the upload config in services/file-uploads:
const aws = require('aws-sdk');
const multer = require('multer');
const multerS3 = require('multer-s3');
aws.config.update({
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
region: 'us-east-1'
})
const s3 = new aws.S3();
const fileFilter = (req, file, cb) => {
if (file.mimetype === 'image/jpeg' || file.mimetype === 'image/png') {
cb(null, true)
} else {
cb(new Error('Invalid Mime Type, only JPEG and PNG'), false);
}
}
const upload = multer({
storage: multerS3({
s3: s3,
bucket: process.env.AWS_BUCKET,
acl: 'public-read',
metadata: function (req, file, cb) {
cb(null, {fieldName: 'TESTING_META_DATA!'});
},
key: function (req, file, cb) {
cb(null, Date.now().toString())
}
})
})
module.exports = upload;
In the request, I see body: { formData: {}, group_id: 1 },
and I can retrieve the group_id with req.body.id, but the formData object is empty
Prepare the FormData like this:
const formData = new FormData();
formData.append('file', this.file); // Append a file
formData.append('group_id', this.currentGroup.id); // Append a field
And then send it like this:
await axios({
method: 'post',
url: '/uploads',
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});

File upload using multer

I am trying to make a web app that you can upload files to using express and multer, and I am having a problem that no files are being uploaded and req.file is always undefined.I have checked multiple solutions but none of them is working.
My reactjs component is as follows:
onSubmit(e) {
e.preventDefault();
const productData = {
name: this.state.name,
image: this.state.image,
description: this.state.description,
category: this.state.category,
quantity: this.state.quantity,
price: this.state.price
};
console.log(productData.image);
this.props.createProduct(productData, this.props.history);
}
onChange(e) {
this.setState({ [e.target.name]: e.target.value });
}
<form onSubmit={this.onSubmit} encType="multipart/form-data">
<UploadImage
name="image"
value={this.state.image}
onChange={this.onChange}
error={errors.image}
type="file"
info="Upload image of the product"
/>
<input
type="submit"
value="Submit"
className="btn btn-info btn-block mt-4"
/>
</form>
My router file is as follows:
const express = require("express");
const router = express.Router();
const multer = require("multer");
const path = require("path");
//Loading validation function
const validateProductInput = require("../../validation/product.js");
//Load product model
const Product = require("../../models/Product");
//#route GET api/products/test
//#desc Test product route
//#access public
router.get("/test", (req, res) => res.json({ msg: "Profile Works" }));
//Creating a multer API to upload images
const storage = multer.diskStorage({
destination: "../../uploads",
filename: function(req, file, cb) {
cb(
null,
file.fieldname + "." + Date.now() + path.extname(file.originalname)
);
}
});
//Init Upload
const upload = multer({
storage: storage
});
//#route Post api/products/add
//#desc Add product
//#access Private
router.post("/add", upload.single("image"), (req, res) => {
const { errors, isValid } = validateProductInput(req.body);
if (!isValid) {
//Return any erros with 400 status
return res.status(400).json(errors);
}
console.log("File: ", req.file);
Product.findOne({
name: req.body.name
}).then(product => {
if (product) {
errors.exist = "Item Already Exists";
return res.status(400).json(errors);
} else {
// let image_path = req.body.image;
// image_path = image_path.replace(
// /fakepath/g,
// "projects\\e-commerce\\MERN-eCommerce\\client\\src\\components\\dashboard\\images"
// );
const newProduct = new Product({
name: req.body.name,
image: req.body.image,
description: req.body.description,
category: req.body.category,
quantity: req.body.quantity,
price: req.body.price
});
newProduct
.save()
.then(product => res.json(product))
.catch(err => res.status(404).json());
}
});
});
//Upload image component
const UploadImage = ({ name, value, type, error, info, onChange }) => {
// Add the following code if you want the name of the file appear on select
$(".custom-file-input").on("change", function() {
var fileName = $(this)
.val()
.split("\\")
.pop();
$(this)
.siblings(".custom-file-label")
.addClass("selected")
.html(fileName);
});
return (
<div className="custom-file">
<input
className={classnames(
"custom-file-input form-control form-control-lg",
{
"is-invalid": error
}
)}
name={name}
type={type}
value={value}
onChange={onChange}
/>
<label className="custom-file-label">Choose file</label>
{info && <small className="form-text text-muted">{info}</small>}
{error && <div className="invalid-feedback">{error}</div>}
</div>
);
};
UploadImage.propTypes = {
name: PropTypes.string.isRequired,
placeholder: PropTypes.string,
value: PropTypes.string.isRequired,
info: PropTypes.string,
error: PropTypes.string,
onChange: PropTypes.func.isRequired
};
export default UploadImage;
console.log("File: ", req.file); is always giving undefined and no file is uploaded to the uploads directory

Express Multer req.files is undefined

Whenever I upload an image, req.files returns undefined.
This is my code:
var upload123 = multer({
dest: path.join(__dirname, 'public/upload/temp'),
});
app.post(upload123);
This is the HTML:
<form action="/images" method="POST" enctype="multipart/form-data">
<div class="panel-body form-horizontal">
<div class="form-group col-md-12">
<label for="file" class="col-md-2 control-label">Browse:</label>
<div class="col-md-10">
<input type="file" class="form-control" id="file" name="file">
</div>
</div>
This is the controller I am trying to access req.files in:
create(req, res) {
const saveImage = function() {
const possible = 'abcdefghijklmnopqrstuvwxyz0123456789';
let imgUrl = '';
for(let i = 0; i < 6; i++) {
imgUrl += possible.charAt(Math.floor(Math.random() * possible.length));
}
Models.Image.find({ filename: imgUrl }, (err, images) => {
if (images.length > 0) saveImage();
else {
var tempPath = req.files.file.path,
ext = path.extname(req.files.file.name).toLowerCase(),
targetPath = path.resolve(`./public/upload/${ imgUrl }${ ext }`);
if (ext === '.png' || ext === '.jpg' || ext === '.jpeg' || ext === '.gif') {
fs.rename(tempPath, targetPath, (err) => {
if (err) throw err;
var newImg = new Models.Image({
title: req.body.title,
description: req.body.description,
filename: imgUrl + ext
});
newImg.save((err, image) => {
res.redirect(`/images/${image.uniqueId}`);
});
});
} else {
fs.unlink(tempPath, () => {
if (err) throw err;
res.json(500, {error: 'Only image files are allowed'});
})
}
}
});
}
saveImage();
}
I am using Express version 4.16.3 and Multer 1.3.0.
I check the docs but couldn't figure out how to simply provide a dest option in Multer. Please help, am new to Nodejs
This is example from code that is working properly:
const multer = require('multer');
const storage = multer.memoryStorage();
const fileUpload = multer({storage});
app.use('/*', fileUpload.single('file'));
If you have this in beginning of your application and you put controllers after this middleware, then in req.file you will have file
The best thing is that it is stored directly in memory, not in some file, so you dont have to load it from filesystem. (which is also safer, especially when you deploy your app to some cloud services and containers, which makes it less visible)
If you insist on your "file-on-disk" solution, based on official docs: https://www.npmjs.com/package/multer you should use it as this:
var upload123 = multer({
dest: path.join(__dirname, 'public/upload/temp'),
});
app.post('/endpoint/to/upload/photos', upload123.single('file'), controller.handleImage);

Resources