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
Related
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>
<% } %>
I am trying to upload a video on youtube from my web app but I am getting an error in sending a file to my backend it is showing the req.file as undefined in req.body it is showing videoFile: "Undefined" and all other values I can see but I am not able to get the file from my react app to backend .
Input.jsx
import axios from "axios";
import React, { useState } from "react";
// import Axios from "axios";
function Input() {
const [form, setForm] = useState({
title: "",
description: "",
file: null,
});
function handleChange(event) {
const InputValue =
event.target.name === "file" ? event.target.file : event.target.value;
setForm({
...form,
[event.target.name]: InputValue,
});
}
function handleSubmit(event) {
event.preventDefault();
console.log({ form });
const headers = {
"content-type": "multipart/form-data",
};
const videoData = new FormData();
videoData.append("videoFile", form.file);
videoData.append("title", form.title);
videoData.append("description", form.description);
axios
.post("http://localhost:3001/upload", videoData, { headers })
.then((res) => {
console.log(res.data);
})
.catch((err) => console.log(err));
}
return (
<div className="form">
<h1>
<i className="fab fa-youtube"></i> Upload video
</h1>
<form onSubmit={handleSubmit} method="POST" enctype="multipart/form-data">
<div>
<input
className="title"
type="text"
placeholder="Title"
name="title"
autoComplete="off"
onChange={handleChange}
/>
</div>
<div>
<textarea
typeof="text"
placeholder="description"
name="description"
autoComplete="off"
onChange={handleChange}
/>
</div>
<div>
<input
type="file"
accept="video/mp4,video/x-m4v,video/*"
placeholder="Add"
name="file"
onChange={handleChange}
/>
</div>
<button className="btn btn-light" type="submit">
Upload Video
</button>
</form>
</div>
);
}
export default Input;
it is node.js part
App.js (backend)
const express = require("express");
const multer = require("multer");
const youtube = require("youtube-api");
const open = require("open");
const cors = require("cors");
const fs = require("fs");
const credentials = require("./client_secret_1013548512515-lti2tpl88m8qqkum1hh095cnevtdi3lu.apps.googleusercontent.com.json");
const app = express();
const bodyParser = require("body-parser");
app.use(express.json());
app.use(cors());
app.use(bodyParser.urlencoded({ extended: true }));
const storage = multer.diskStorage({
destination: "./",
filename: (req, file, cb) => {
const newFileName = `${Date.now()}-${file.originalname}`;
cb(null, newFileName);
},
});
const uploadVideoFile = multer({
storage: storage,
}).single("videoFile");
app.post("/upload", uploadVideoFile, (req, res) => {
console.log(req.body);
console.log(req.file); //it is showing undefined
if (req.file) { //getting error in this part
const filename = req.file.filename;
const { title, description } = req.body;
open(
oAuth.generateAuthUrl({
access_type: "offline",
scope: "https://www.googleapis.com/auth/youtube.upload",
state: JSON.stringify({
filename,
title,
description,
}),
})
);
}
});
app.get("/oauth2callback", (req, res) => {
res.redirect("http://localhost:3000/success");
const { filename, title, description } = JSON.parse(req.query.state);
oAuth.getToken(req.query.code, (err, tokens) => {
if (err) {
console.log(err);
return;
}
oAuth.setCredentials(tokens);
youtube.videos.insert(
{
resource: {
snippet: { title, description },
status: { privacyStatus: "private" },
},
part: "snippet,status",
media: {
body: fs.createReadStream(filename),
},
},
(err, data) => {
console.log("Done");
process.exit();
}
);
});
});
const oAuth = youtube.authenticate({
type: "oauth",
client_id: credentials.web.client_id,
client_secret: credentials.web.client_secret,
redirect_url: credentials.web.redirect_uris[0],
});
PORT = 3001;
app.listen(PORT, () => {
console.log("app is listening on port 3001");
});
Please make the following changes in the Input.jsx file.
// 1. Inside the handleChange() function
const InputValue = event.target.name === "file" ? event.target.files : event.target.value;
// 2. Inside the handleSubmit() function
videoData.append("videoFile", form.file[0], form.file[0].name);
This should output the file object inside req.file.
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.).
I'm trying to send a file/image from React to node.js server with multer. The problem is I can send an image only through Postman but when I'm trying the same in React I'm getting: TypeError: Cannot read property 'path' of undefined. I don't understand whether I should send a file/image in binary or use another format. I've already tried to use reader.readAsDataURL() and reader.readAsBinaryString() but it didn't work.
const multer = require("multer");
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, "./uploads/");
},
filename: (req, file, cb) => {
cb(null, new Date().toISOString() + file.originalname);
}
});
const fileFilter = (req, file, cb) => {
if (
file.mimetype === "image/jpeg" ||
file.mimetype === "image/png" ||
file.mimetype === "image/jpg"
) {
cb(null, true);
}
cb(null, false);
};
const upload = multer({
storage: storage,
limits: { fileSize: 5000000 },
fileFilter: fileFilter
});
// Create a post
router.post(
"/",
upload.single("image"),
passport.authenticate("jwt", { session: false }),
(req, res) => {
const { errors, isValid } = validationPostInput(req.body);
if (!isValid) {
return res.status(400).json(errors);
}
console.log(req.file);
const newPost = new Post({
text: req.body.text,
theme: req.body.theme,
name: req.body.name,
avatar: req.body.avatar,
image: req.file.path,
user: req.user.id
});
newPost.save().then(post => res.json(post));
}
);
// CREATE A POST
export const createPost = (userInput, history) => dispatch => {
const headers = {
"Content-Type": "form-data"
};
axios
.post("/post", userInput, headers)
.then(res => history.push("/post/all"))
.catch(err =>
dispatch({
type: GET_ERRORS,
payload: err.response.data
})
);
};
import React, { Component } from "react";
import PropTypes from "prop-types";
// import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { createPost } from "../../actions/postActions";
import "./style.css";
class CreatePost extends Component {
state = {
text: "",
theme: "",
image: "",
errors: {}
};
componentWillReceiveProps(nextProps) {
if (nextProps.errors) {
this.setState({ errors: nextProps.errors });
}
}
onSubmit = e => {
e.preventDefault();
const { user } = this.props.auth;
const newPost = {
text: this.state.text,
theme: this.state.theme,
image: this.state.image,
name: user.username,
avatar: user.avatar
};
this.props.createPost(newPost);
};
onChange = e => this.setState({ [e.target.name]: e.target.value });
fileSelectHandler = e => {
const param = e.target.files[0];
let reader = new FileReader();
reader.readAsDataURL(param);
this.setState({
image: reader.result
});
console.log(reader);
};
render() {
const { text, theme, errors } = this.state;
return (
<section className="post">
<form
onSubmit={this.onSubmit}
className="post__form"
action="/post"
method="POST"
encType="multipart/form-data"
>
<div className="post__form--input">
<label>Theme</label>
<input
type="text"
name="theme"
value={theme}
onChange={this.onChange}
/>
{errors && <small>{errors.theme}</small>}
</div>
<div className="post__form--input">
<label>Text</label>
<textarea
type="text"
name="text"
value={text}
onChange={this.onChange}
/>
{errors && <small>{errors.text}</small>}
</div>
<div className="post__form--file">
<label>Add Image</label>
<input
type="file"
name="file"
accept=".png, .jpg"
onChange={this.fileSelectHandler}
/>
</div>
<button type="submit" className="button">
Submit
</button>
</form>
</section>
);
}
}
CreatePost.propTypes = {
errors: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
errors: state.errors,
auth: state.auth
});
export default connect(
mapStateToProps,
{ createPost }
)(CreatePost);
Finally, I found out how to solve that problem.
onSubmit = e => {
e.preventDefault();
const { user } = this.props.auth;
const form = new FormData();
form.append("file", this.state.file);
form.append("theme", this.state.theme);
form.append("text", this.state.text);
form.append("name", user.username);
form.append("avatar", user.avatar);
this.props.createPost(form);
}
You are sending the field name as file:
<input
type="file"
name="file"
accept=".png, .jpg"
onChange={this.fileSelectHandler}
/>
But you are setting the field name as "image" on multer:
upload.single("image")
You need to change it to:
upload.single("file")
The correct syntax is:
.single(fieldname)
Accept a single file with the name fieldname. The single file will be
stored in req.file.
I’ve spent most of a day looking into this and trying to make it work. This is an app with a React/Redux front end, and a Node/Express/Mongoose/MongoDB back end.
I currently have a Topics system where an authorized user can follow/unfollow topics, and an admin can Add/Remove topics.
I want to be able to upload an image file when submitting a new topic, and I want to use Cloudinary to store the image and then save the images path to the DB with the topic name.
The problem I am having is that I am unable to receive the uploaded file on the back end from the front end. I end up receiving an empty object, despite tons of research and trial/error. I haven’t finished setting up Cloudinary file upload, but I need to receive the file on the back end before even worrying about that.
SERVER SIDE
index.js:
const express = require("express");
const http = require("http");
const bodyParser = require("body-parser");
const morgan = require("morgan");
const app = express();
const router = require("./router");
const mongoose = require("mongoose");
const cors = require("cors");
const fileUpload = require("express-fileupload");
const config = require("./config");
const multer = require("multer");
const cloudinary = require("cloudinary");
const cloudinaryStorage = require("multer-storage-cloudinary");
app.use(fileUpload());
//file storage setup
cloudinary.config({
cloud_name: "niksauce",
api_key: config.cloudinaryAPIKey,
api_secret: config.cloudinaryAPISecret
});
const storage = cloudinaryStorage({
cloudinary: cloudinary,
folder: "images",
allowedFormats: ["jpg", "png"],
transformation: [{ width: 500, height: 500, crop: "limit" }] //optional, from a demo
});
const parser = multer({ storage: storage });
//DB setup
mongoose.Promise = global.Promise;
mongoose.connect(
`mongodb://path/to/mlab`,
{ useNewUrlParser: true }
);
mongoose.connection
.once("open", () => console.log("Connected to MongoLab instance."))
.on("error", error => console.log("Error connecting to MongoLab:", error));
//App setup
app.use(morgan("combined"));
app.use(bodyParser.json({ type: "*/*" }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(cors());
router(app, parser);
//Server setup
const port = process.env.PORT || 3090;
const server = http.createServer(app);
server.listen(port);
console.log("server listening on port: ", port);
TopicController/CreateTopic
exports.createTopic = function(req, res, next) {
console.log("REQUEST: ", req.body); //{ name: 'Topic with Image', image: {} }
console.log("IMAGE FILE MAYBE? ", req.file); //undefined
console.log("IMAGE FILES MAYBE? ", req.files); //undefined
const topic = new Topic(req.body);
if (req.file) {
topic.image.url = req.file.url;
topic.image.id = req.file.publid_id;
} else {
console.log("NO FILE UPLOADED");
}
topic.save().then(result => {
res.status(201).send(topic);
});
};
router.js
module.exports = function(app, parser) {
//User
app.post("/signin", requireSignin, Authentication.signin);
app.post("/signup", Authentication.signup);
//Topic
app.get("/topics", Topic.fetchTopics);
app.post("/topics/newTopic", parser.single("image"), Topic.createTopic);
app.post("/topics/removeTopic", Topic.removeTopic);
app.post("/topics/followTopic", Topic.followTopic);
app.post("/topics/unfollowTopic", Topic.unfollowTopic);
};
CLIENT SIDE
Topics.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import { Loader, Grid, Button, Icon, Form } from "semantic-ui-react";
import {
fetchTopics,
followTopic,
unfollowTopic,
createTopic,
removeTopic
} from "../actions";
import requireAuth from "./hoc/requireAuth";
import Background1 from "../assets/images/summer.jpg";
import Background2 from "../assets/images/winter.jpg";
const compare = (arr1, arr2) => {
let inBoth = [];
arr1.forEach(e1 =>
arr2.forEach(e2 => {
if (e1 === e2) {
inBoth.push(e1);
}
})
);
return inBoth;
};
class Topics extends Component {
constructor(props) {
super(props);
this.props.fetchTopics();
this.state = {
newTopic: "",
selectedFile: null,
error: ""
};
}
onFollowClick = topicId => {
const { id } = this.props.user;
this.props.followTopic(id, topicId);
};
onUnfollowClick = topicId => {
const { id } = this.props.user;
this.props.unfollowTopic(id, topicId);
};
handleSelectedFile = e => {
console.log(e.target.files[0]);
this.setState({
selectedFile: e.target.files[0]
});
};
createTopicSubmit = e => {
e.preventDefault();
const { newTopic, selectedFile } = this.state;
this.props.createTopic(newTopic.trim(), selectedFile);
this.setState({
newTopic: "",
selectedFile: null
});
};
removeTopicSubmit = topicId => {
this.props.removeTopic(topicId);
};
renderTopics = () => {
const { topics, user } = this.props;
const followedTopics =
topics &&
user &&
compare(topics.map(topic => topic._id), user.followedTopics);
console.log(topics);
return topics.map((topic, i) => {
return (
<Grid.Column className="topic-container" key={topic._id}>
<div
className="topic-image"
style={{
background:
i % 2 === 0 ? `url(${Background1})` : `url(${Background2})`,
backgroundRepeat: "no-repeat",
backgroundPosition: "center",
backgroundSize: "cover"
}}
/>
<p className="topic-name">{topic.name}</p>
<div className="topic-follow-btn">
{followedTopics.includes(topic._id) ? (
<Button
icon
color="olive"
onClick={() => this.onUnfollowClick(topic._id)}
>
Unfollow
<Icon color="red" name="heart" />
</Button>
) : (
<Button
icon
color="teal"
onClick={() => this.onFollowClick(topic._id)}
>
Follow
<Icon color="red" name="heart outline" />
</Button>
)}
{/* Should put a warning safety catch on initial click, as to not accidentally delete an important topic */}
{user.isAdmin ? (
<Button
icon
color="red"
onClick={() => this.removeTopicSubmit(topic._id)}
>
<Icon color="black" name="trash" />
</Button>
) : null}
</div>
</Grid.Column>
);
});
};
render() {
const { loading, user } = this.props;
if (loading) {
return (
<Loader active inline="centered">
Loading
</Loader>
);
}
return (
<div>
<h1>Topics</h1>
{user && user.isAdmin ? (
<div>
<h3>Create a New Topic</h3>
<Form
onSubmit={this.createTopicSubmit}
encType="multipart/form-data"
>
<Form.Field>
<input
value={this.state.newTopic}
onChange={e => this.setState({ newTopic: e.target.value })}
placeholder="Create New Topic"
/>
</Form.Field>
<Form.Field>
<label>Upload an Image</label>
<input
type="file"
name="image"
onChange={this.handleSelectedFile}
/>
</Form.Field>
<Button type="submit">Create Topic</Button>
</Form>
</div>
) : null}
<Grid centered>{this.renderTopics()}</Grid>
</div>
);
}
}
const mapStateToProps = state => {
const { loading, topics } = state.topics;
const { user } = state.auth;
return { loading, topics, user };
};
export default requireAuth(
connect(
mapStateToProps,
{ fetchTopics, followTopic, unfollowTopic, createTopic, removeTopic }
)(Topics)
);
TopicActions/createTopic:
export const createTopic = (topicName, imageFile) => {
console.log("IMAGE IN ACTIONS: ", imageFile); //this is still here
// const data = new FormData();
// data.append("image", imageFile);
// data.append("name", topicName);
const data = {
image: imageFile,
name: topicName
};
console.log("DATA TO SEND: ", data); //still shows image file
return dispatch => {
// const config = { headers: { "Content-Type": "multipart/form-data" } };
// ^ this fixes nothing, only makes the problem worse
axios.post(CREATE_NEW_TOPIC, data).then(res => {
dispatch({
type: CREATE_TOPIC,
payload: res.data
});
});
};
};
When I send it like this, I receive the following on the back end:
(these are server console.logs)
REQUEST: { image: {}, name: 'NEW TOPIC' }
IMAGE FILE MAYBE? undefined
IMAGE FILES MAYBE? undefined
NO FILE UPLOADED
If I go the new FormData() route, FormData is an empty object, and I get this server error:
POST http://localhost:3090/topics/newTopic net::ERR_EMPTY_RESPONSE
export const createTopic = (topicName, imageFile) => {
console.log("IMAGE IN ACTIONS: ", imageFile);
const data = new FormData();
data.append("image", imageFile);
data.append("name", topicName);
// const data = {
// image: imageFile,
// name: topicName
// };
console.log("DATA TO SEND: ", data); // shows FormData {} (empty object, nothing in it)
return dispatch => {
// const config = { headers: { "Content-Type": "multipart/form-data" } };
// ^ this fixes nothing, only makes the problem worse
axios.post(CREATE_NEW_TOPIC, data).then(res => {
dispatch({
type: CREATE_TOPIC,
payload: res.data
});
});
};
};
Solution was to switch to using Firebase instead, and deal with image upload on the React client (this was attempted with cloudinary but with no success). The resulting download url can be saved to the database with the topic name (which is all I wanted from cloudinary) and now it is displaying the correct images along with the topics.