So I am trying to upload a profile Image via react.js app to a backend running as a REST API using node.js express.js and sqlite as a DB.
But somehow no tutorial I can find will work for me. I must be close though. So what I have right now is this:
My routes look like this:
playerRoutes.js:
const express = require('express');
const router = express.Router();
const multer = require('multer');
const uuidv4 = require('uuid/v4');
const upload_dir = './uploads';
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, upload_dir);
},
filename: (req, file, cb) => {
cb(null, `${uuidv4()}-${file.filename.toLowerCase}`);
}
});
const upload = multer({
storage: storage,
fileFilter: (req, file, cb) => {
if (
file.mimetype == 'image/png' ||
file.mimetype == 'image/jpg' ||
file.mimetype == 'image/jpeg'
) {
cb(null, true);
} else {
cb(null, false);
return cb(new Error('Only .png, .jpg and .jpeg format allowed!'));
}
}
});
const { playerController } = require('../controller');
router.get('/', playerController.getAllPlayer);
router.get('/:id', playerController.getSpecificPlayer);
router.post('/', upload.single('profileImg'), playerController.createPlayer);
router.put('/:id', playerController.updatePlayer);
router.delete('/:id', playerController.deletePlayer);
module.exports = router;
So key points are the setup of storage
which tells where to upload + the file filter in upload
, right?
And the route.post
which will `upload.single('profileImg'), right? the route will include my controller for create Player which can be found here:
playerController.js
const { Player } = require('../models');
module.exports = {
getAllPlayer(req, res) {
return Player.findAll({
attributes: ['id', 'name', 'nickname', 'profileImg']
})
.then(players => res.status(200).send(players))
.catch(err => res.status(400).send(err));
},
getSpecificPlayer(req, res) {
return Player.findByPk(req.params.id, {
attributes: ['id', 'name', 'nickname', 'profileImg']
}).then(player => {
if (!player) {
return res.status(404).send({
message: `Player with id ${req.params.id} not found`
});
}
return res.status(200).send(player);
});
},
createPlayer(req, res) {
console.log(req.file);
return Player.create({
name: req.body.name,
nickname: req.body.nickname
})
.then(player => res.status(201).send(player))
.catch(err => res.status(400).send(err));
},
updatePlayer(req, res) {
return Player.findByPk(req.params.id, {
attributes: ['id', 'name', 'nickname', 'profileImg']
})
.then(player => {
if (!player) {
return res.status(404).send({
message: `Player with id ${req.params.id} not found`
});
}
return player
.update({
name: req.body.name || player.name,
nickname: req.body.nickname || player.nickname,
profileImg: req.body.profileImg || player.profileImg
})
.then(() => res.status(200).send(player))
.catch(err => res.status(400).send(err));
})
.catch(err => res.status(400).send(err));
},
deletePlayer(req, res) {
return Player.findByPk(req.params.id, {
attributes: ['id', 'name', 'nickname', 'profileImg']
})
.then(player => {
if (!player) {
return res.status(404).send({
message: `Player with id ${req.params.id} not found`
});
}
return player
.destroy()
.then(() => res.status(204).send())
.catch(err => res.status(400).send(err));
})
.catch(err => res.status(400).send(err));
}
};
For now, regarding to several tutorials I should see information within console.log(req.file)
when hitting the endpoint with post, right?
Model looks like this:
models/player.js (sequelize model)
'use strict';
module.exports = (sequelize, DataTypes) => {
const Player = sequelize.define(
'Player',
{
name: {
type: DataTypes.STRING,
validate: { notEmpty: { msg: 'Player name cannot be empty' } }
},
nickname: {
type: DataTypes.STRING
},
profileImg: {
type: DataTypes.STRING
}
},
{}
);
Player.associate = function(models) {
// associations can be defined here
};
return Player;
};
So now to the frontend code (react.js):
This is the form I am loading in my react app:
playerform.js
import React, { Component } from 'react';
import axios from 'axios';
import {
FormControl,
FormGroup,
FormLabel,
Form,
Button
} from 'react-bootstrap';
import PropTypes from 'prop-types';
class PlayerForm extends Component {
state = {
name: '',
nickname: '',
profileImg: ''
};
handleSubmit = e => {
e.preventDefault();
axios
.post(`${process.env.API_URL}/player`, JSON.stringify(this.state), {
headers: {
Accept: 'application/json',
'Content-Type': 'application/json'
}
})
.then(() => this.props.onCreate())
.catch(err => console.log(err));
};
onFileChange(e) {
this.setState({ profileImg: e.target.files[0] });
}
handelChange = e => {
this.setState({
[e.target.name]: e.target.value
});
};
render() {
return (
<div className="container">
<Form onSubmit={this.handleSubmit}>
<FormGroup>
<FormLabel>Name</FormLabel>
<FormControl
type="text"
name="name"
placeholder="Enter player name"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<FormGroup>
<FormLabel>Nickname</FormLabel>
<FormControl
type="text"
name="nickname"
placeholder="Enter player nickname"
onChange={this.handelChange.bind(this)}
/>
</FormGroup>
<FormGroup>
<FormLabel>Picture</FormLabel>
<FormControl
type="file"
name="file"
playerholder="Upload player picture"
onChange={this.onFileChange.bind(this)}
/>
</FormGroup>
<Button variant="btn btn-primary" type="submit">
Add
</Button>
</Form>
</div>
);
}
}
PlayerForm.propTypes = {
onCreate: PropTypes.func.isRequired
};
export default PlayerForm;
So when entering things and hitting the Add
Button my api states that req.file is undefined and I cannot find out why.
Can anyone help me drilling down the error?