Uploading a picture with React.js to store in MongoDB - javascript

I am trying to upload an image file from client side to MongoDB.
I am using express server with 'multer' and 'sharp' to uploade image.
I am using cra app for client side.
Using Postman, I can upload an image and it is stored in MongoDB.
now I am trying to upload an image from client side, which is not going well.
this is how I change the inputs, using Hook.
const INITIAL_STATE = {
bday: '',
occupation: '',
introduction: '',
picture: undefined,
};
const [formData, setFormData] = useState(INITIAL_STATE);
const { bday, occupation, introduction, picture } = formData;
const handleChange = (event) => {
const { name, value } = event.target;
setFormData({ ...formData, [name]: value });
};
and the input for file upload is this.
<div className='form-group'>
<input
className='form-input bg-light'
name='picture'
type='file'
value={picture}
onChange={handleChange}
/>
</div>
but I have this error message as soon as I select the file
Warning: A component is changing an uncontrolled input of type file to
be controlled.
If I just submit the form ignoring that error message, I cannot upload the file.this is how i upload the file from client side.(proxy: localhost:5000)
const formData = new FormData();
formData.append('avatar', picture);
const config = {
headers: {
'Content-Type': 'multipart/form-data',
},
};
await axios.post('/profile/avatar', formData, config);
When I use Postman upload the file, setting up like following, it works fine.
What should I do to upload an image file?
thank you in advance.

Your code isn't working, because your handleChange function isn't working with a input[type="file"]. In your handleChange() you set the value of your state to event.target.value but your file input stores the file in event.target.files (if you use only one file use e.target.files[0].

Hello change your handleChange to:
const handleChange = () => event => {
console.log(event.target.value)
}
and for the warning it is because of the value change it form
<input
className='form-input bg-light'
name='picture'
type='file'
value={picture}
onChange={handleChange}
/>
to:
<input
className='form-input bg-light'
name='picture'
type='file'
value={formData.picture}
onChange={handleChange}
/>

Related

how can i upload multiple files to s3 using nuxtjs

please I'm lost how can I upload multiple files
This is how I upload single files:
<div class="a-row a-spacing-top-medium">
<label class="choosefile-button">
<i class="fal fa-plus"></i>
<input
type="file"
#change="onFileSelected"
ref="files"
/>
<p style="margin-top: -70px">{{ fileName }}</p>
</label>
</div>
this is my script tag for image upload:
<script>
import axios from 'axios'
export default {
data() {
return {
selectedFile: null,
fileName: '',
photos: null
}
},
methods: {
onFileSelected(event) {
this.selectedFile = event.target.files[0]
console.log(this.selectedFile)
this.fileName = event.target.files[0].name
},
async onAddProduct() {
let data = new FormData()
data.append('photos', this.selectedFile, this.selectedFile.name)
const response = await axios
.post('http://localhost:5000/api/products', data)
.then(() => {
console.log(response)
})
}
}
}
</script>
each time I add multiple to my input tag it just uploads an image in my browser.
Please how can I go about multiple uploads?
Here's a basic blog post on how to upload a files to S3 in Vue through a node backend. This may help you out.
Basically what you need to do is create a backend that handles the files and uploads them. This can be done using multer, multer-s3 and node.
One thing that you should also change is just having
data.append('photos', this.selectedFile)
data.append('fileName', this.selectedFile.name)

Javascript file upload e.target == null when choosing another file after initially chose one

I've currently implemented the file uploading using <input> and for some reason when I try to change the file after already chosen the file. The website will crash stating the even.target is null.
<Button label="Upload S3 File">
<input type="file" onInput={(e) => handleFileUpload(e)} />
</Button>
And here's my handler function, to be exact, the error happens on
file: e.target.files ? e.target.files[0] : null,
// Set up the handler
const [formValues, setFormValues] = useState<RequestData>({
overrideUbi: "",
marketplace: "",
isTier1: "",
brandName: "",
brandURL: "",
file: null
});
const handleFormChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = event.target;
// console.log(event);
setFormValues((prevState) => ({
...prevState,
[name]: value,
}));
};
const handleFileUpload = (e: any) => {
console.log("upload");
console.log(e.target)
console.log(e.target.files)
if (!e.target.files) return;
setFormValues((prevFormValues) => ({
...prevFormValues,
file: e.target.files ? e.target.files[0] : null,
}));
};
Not sure why it states e.target is null if I choose another file after have one file chosen already. My if statement for checking e.target == null also doesn't work preventing the website from crashing.
Thanks!
Update:
I've attached a couple of screenshot showing the exact error.
One interesting finding is that if I use e.persist(), I can change the file I want to upload without triggering this error. I don't know why exactly that is, if someone can explain what is going on in this lifecycle I would truly appreciate it. Thanks!
try e.currentTarget.files, if you have a reference set on the input component.

React useState Hook - Can't Clear State on Form Data

Slightly novice react hooks user here. Trying to submit a blog post form, consisting of a title, author and URL. I can submit my form correctly and values are saving in database, but I can't clear the data from the form.
I'm holding state like this
const [newBlog, setNewBlog] = useState({ url: "", author: "", title: "" });
and handling form change to update state like this:
const handleBlogChange = e => {
const { name, value } = e.target;
setNewBlog({ ...newBlog, [name]: value });
};
example setup of one my input fields (identical across all three)
<form className="form" onSubmit={addBlog}>
<div className="col-lg-9">
<input
className="form-control"
id="title"
aria-describedby="emailHelp"
placeholder="Enter title"
name="title"
value={newBlog.title}
onChange={handleBlogChange}
/>
</div>
and this is my addBlog function. you can see where I commented out one version of clearing the state which is causing problems.
const addBlog = event => {
event.preventDefault();
const { url, title, author } = newBlog;
const blogObject = {
url,
title,
author
};
blogService
.create(blogObject)
.then(data => {
setBlogs(blogs.concat(data));
showMessage(`Success! ${newBlog.title} by ${newBlog.author} was added`);
// setNewBlog([...newBlog, { url: "", title: "", author: "" }]);
})
.catch(error => {
showMessage(
`Sorry can't add blog. Here's why: ${error.response.data.error}`,
false
);
});
};
I've also tried different variations which are leaving the data in the form, no errors, but not clearing out the form. Examples of this include
setNewBlog(''), setNewBlog([]) and
setNewBlog(newBlog.title='')
Nothing is working.

make http post request with reactJs and NodeJs

I have a registration form in one of my reactJs files, which takes all values of input fields (works as I expect).
SignUp.js
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
class SignUpForm extends Component {
constructor() {
super();
this.state = {
email: '',
password: '',
username: '',
hasAgreed: false,
formErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(e) {
let target = e.target;
let value = target.type === 'checkbox' ? target.checked : target.value;
let name = target.name;
this.setState({[name]: value});
}
handleSubmit(e) {
e.preventDefault();
console.log('The form was submitted with the following data:');
console.log([this.state.email, cryptr.encrypt(this.state.password), this.state.username, this.state.hasAgreed]);
//I want to send my above data to node server
}
render() {
return (
<div className="FormCenter">
<form onSubmit={this.handleSubmit} className="FormFields" method="POST" action="/register">
<div className="FormField">
<label htmlFor="name">Username</label>
<input type="text" id="name" placeholder="Enter your username" name="username" value={this.state.username} onChange={this.handleChange} />
</div>
<div className="FormField">
<label htmlFor="password">Password</label>
<input type="password" id="password" placeholder="Enter your password" name="password" value={this.state.password} onChange={this.handleChange} />
<div className="FormField">
<label htmlFor="email">E-Mail Address</label>
<input type="email" id="email" placeholder="Enter your email" name="email" value={this.state.email} onChange={this.handleChange} />
</div>
</div>
<div className="FormField">
<label>
<input type="checkbox" name="hasAgreed" value={this.state.hasAgreed} onChange={this.handleChange} /> I agree all statements in terms of service
</label>
</div>
</form>
</div>
);
}
}
export default SignUpForm;
Now I want to send the mentioned data to the created node server.
server.js
const express = require("express");
const bodyParser = require("body-parser");
const path = require("path");
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(express.static(path.join(__dirname, "src/containers/user")));
//the request in which I expect input data
app.post('/register', (req, res) => {
if (!req.body) return res.sendStatus(400);
console.log(req.body, "body");
res.send('welcome, ' + req.body.username)
});
app.listen(5000, () => {
console.log("is listening on port 5000");
});
As I expect, in form tag writing method="POST" action="/register"would do it's job, but even the console.log from /register request is not responding.
Note: The next thing I should implement is to write all the data in txt file. So fetching the data in back end is a must.
What you need to do is pass the data as an object to axios like so, which you indicated you have installed in your project.
const formData = {
email: this.state.email,
password: cryptr.encrypt(this.state.password)
username: this.state.username,
hasAgreed: this.state.hasAgreed
}
axios({
method: 'post',
url: '/register',
data: formData,
config: { headers: {'Content-Type': 'multipart/form-data' }}
})
.then(function (response) {
//handle success
})
.catch(function (response) {
//handle error
});
The default behavior of a form is to submit when a submit button is clicked, or if a user hits enter. In your case however, you have a submit handler like so;
handleSubmit(e) {
e.preventDefault();
console.log('The form was submitted with the following data:');
console.log([this.state.email, cryptr.encrypt(this.state.password), this.state.username, this.state.hasAgreed]);
//I want to send my above data to node server
}
In this handler your are writing e.preventDefault(); which as the name implies, prevents the form's default behavior from happening. As a result no request is actually being made to the server.
Now the surest way I know to make this work, would be to use some kind of ajax library. You can use fetch, or something like axios.
The other way which MAY work is to remove the e.preventDefault();. Now this way is a bit more tricky. Usually react apps are not served from the same server as the api, in which case your request's url would need to look something more like this.
http://localhost:5000/register. Alternatively, you can tell webpack dev server to proxy your requests. Even then I personally am not sure this would work simply because I have never tried it.
Its important to note, either approach you choose will require the full url in the request if the react app is not served from the same place as the api, or if there is no proxying done by wepback. In other words, you may very well need http://localhost:5000/register.
My suggestion would be to use an ajax library like I mentioned.
Hope this helps.

Upload and read a file in react

Im trying to upload a file with React and see its contents, but what it gives me is C:\fakepath\. I know why it gives fakepath, but what is the correct way to upload and read the contents of a file in react?
<input type="file"
name="myFile"
onChange={this.handleChange} />
handleChange: function(e) {
switch (e.target.name) {
case 'myFile':
const data = new FormData();
data.append('file', e.target.value);
console.log(data);
default:
console.error('Error in handleChange()'); break;
}
},
To get the file info you want to use event.target.files which is an array of selected files. Each one of these can be easily uploaded via a FormData object. See below snippet for example:
class FileInput extends React.Component {
constructor(props) {
super(props)
this.uploadFile = this.uploadFile.bind(this);
}
uploadFile(event) {
let file = event.target.files[0];
console.log(file);
if (file) {
let data = new FormData();
data.append('file', file);
// axios.post('/files', data)...
}
}
render() {
return <span>
<input type="file"
name="myFile"
onChange={this.uploadFile} />
</span>
}
}
ReactDOM.render(<FileInput />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js"></script>
<div id="root"></div>
You may want to look into FileReader which can help if you want to handle the file on the client side, for example to display an image.
https://developer.mozilla.org/en-US/docs/Web/API/FileReader
You can use React Dropzone Uploader, which gives you file previews (including image thumbnails) out of the box, and also handles uploads for you.
In your onChangeStatus prop you can react to the file's meta data and the file itself, which means you can do any kind of client-side processing you want before or after uploading the file.
import 'react-dropzone-uploader/dist/styles.css'
import Dropzone from 'react-dropzone-uploader'
const Uploader = () => {
return (
<Dropzone
getUploadParams={() => ({ url: 'https://httpbin.org/post' })} // specify upload params and url for your files
onChangeStatus={({ meta, file }, status) => { console.log(status, meta, file) }}
onSubmit={(files) => { console.log(files.map(f => f.meta)) }}
accept="image/*,audio/*,video/*"
/>
)
}
Uploads have progress indicators, and they can be cancelled or restarted. The UI is fully customizable.
Full disclosure: I wrote this library.
Try to use Multer and gridfs-storage on the back end and store the fileID along with your mongoose schema.
// Create a storage object with a given configuration
const storage = require('multer-gridfs-storage')({
url: 'MONGOP DB ATLAS URL'
});
// Set multer storage engine to the newly created object
const upload = multer({ storage }).single('file');
router.post('/', upload, (req, res) => {
const newreminder = new Reminders({
category: req.body.category,
name:req.body.name,
type: req.body.type,
exdate: req.body.exdate,
location:req.body.location,
notes:req.body.notes,
fileID: req.file.id
});
newreminder.save(function(err){
if(err){
console.log(err);
return;
}
res.json({ "success": "true"});
});
});
Then on the front end treat it normally (with Axios) and upload the entire file and grab a hold of all the info in the normal react way:
onSubmit = (e) => {
e.preventDefault;
const formData = new FormData();
formData.append({ [e.target.name]: e.target.value })
formData.append('file', e.target.files[0]);
axios.post({
method:'POST',
url:'EXPRESS JS POST REQUEST PATH',
data: formData,
config:{ headers: {'Content-Type':'multipart/form-data, boundary=${form._boundary}'}}
})
.then(res => console.log(res))
.catch(err => console.log('Error', err))
}
Have you use dropzone ?
see this react-dropzone
easy implement, upload and return url if this important.
onDrop: acceptedFiles => {
const req = request.post('/upload');
acceptedFiles.forEach(file => {
req.attach(file.name, file);
});
req.end(callback);
}
You can use FileReader onload methods to read the file data and then can send it to the server.
You can find this useful to handle files using File Reader in React ReactJS File Reader
To add to the other answers here, especially for anyone new to React, it is useful to understand that react handles forms a little differently than people may be used to.
At a high level, react recommends using 'Controlled components" :
In most cases, we recommend using controlled components to implement forms. In a controlled component, form data is handled by a React component. The alternative is uncontrolled components, where form data is handled by the DOM itself.
This essentially means that the user input, e.g. a text field, is also a state of the component and as the user updates it the state is updated and the value of the state if displayed in the form. This means the state and the form data are always in synch.
For an input type of file this will not work because the file input value is read-only. Therefore, a controlled component cannot be used and an 'uncontrolled component' is used instead.
In React, an is always an uncontrolled component because its value can only be set by a user, and not programmatically.
The recommended way to input a file type (at the time of writing) is below, from the react documentation here https://reactjs.org/docs/uncontrolled-components.html#the-file-input-tag:
class FileInput extends React.Component {
constructor(props) {
super(props);
this.handleSubmit = this.handleSubmit.bind(this);
this.fileInput = React.createRef();
}
handleSubmit(event) {
event.preventDefault();
alert(
`Selected file - ${this.fileInput.current.files[0].name}`
);
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Upload file:
<input type="file" ref={this.fileInput} />
</label>
<br />
<button type="submit">Submit</button>
</form>
);
}
}
ReactDOM.render(
<FileInput />,
document.getElementById('root')
);
The documentation includes a codepen example which can be built on.

Categories

Resources