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.
Related
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)
I was exploring Remix.run and making a sample app. I came across an issue that has been bothering me for some time now. Correct me if I am wrong: action() is for handling Form submission, and loader() is for fetching data initially.
For my application, I used mongoose to connect MongoDB, defined models, and defined querying function in a query.server.ts file. I want to fetch data from the database through a function defined in the query.server.ts file when an image is clicked on the UI. How can I do that without using forms? I cannot pre-fetch the data without knowing what image was clicked by the user.
You can create a resource route. These are like regular routes, but don't export a default component (no UI).
You can use the useFetcher hook and call fetcher.load() to call your resource route. The data is in fetcher.data.
// routes/query-data.ts
export const loader: LoaderFunction = async ({request}) => {
const url = new URL(request.url)
const img = url.searchParams.get('img')
const data = await getData(img)
return json(data)
}
// routes/route.tsx
export default function Route() {
const fetcher = useFetcher()
const handleImgClick = (e) => {
const img = e.target
fetcher.load(`/query-data?img=${img.attr('src')}`)
}
return (
<div>
<img onClick={handleImageClick} src="/images/file.jpg" />
<pre>{ JSON.stringify(fetcher.data, null, 2) }</pre>
</div>
)
}
I have a problem with uploading a file from my pc via ngForm as part of a small project I do for a class. It seems that I cannot correctly upload the file as during debugging I get "fakedirectory/name-of-file" rather than the actual temp directory.
I did already search some related posts but they seem to be different than my case and I cannot get them to work.
I would greatly appreciate any help and guidance what can I try next.
I have a frontend part of the project and a separate rest api backend. I will paste the related code here:
HTML:
<form #addOfferForm="ngForm" (ngSubmit)="submitNewOffer(addOfferForm)">
Here I have other text inputs that work fine
...
<input (I tried with ngForm and without) type="file" accept="image/png" id="offerPhoto" (change)="handleOfferPhotoUpload($event)">
...
<button class="public">Publish</button>
Component:
offerPhoto?: File
handleOfferPhotoUpload(event: InputEvent){
const input: HTMLInputElement = event.target as HTMLInputElement;
this.offerPhoto = input.files[0]
console.log( "this.offerPhoto" + this.offerPhoto)
}
addOfferForm: FormGroup = new FormGroup({
offerName: new FormControl(''),
...
offerPhoto: new FormControl(''),
})
submitNewOffer(addOfferForm: NgForm): void {
this.offerService.addOffer$( addOfferForm.value).subscribe({
next: (offer) => {
this.router.navigate(['/offers'])
},
error: (error) => {
console.error(error)
}
})
Service:
addOffer$(body: { offerName: string, ... offerPhoto: File }): Observable<IOffer> //This is an interface that I use {
return this.http.post<IOffer>(`${apiUrl}/offers`, body, { withCredentials: true });
}
Then on the backend I have:
function createOffer(req, res, next) {
const { offerName, buyOrSell, cameraOrLens, offerDescription, offerPhoto, offerContact } = req.body;
const { _id: userId } = req.user;
uploadFile(offerPhoto).then(id => {
const offerPhoto = `https://drive.google.com/uc?id=${id}`
return offerModel.create({ offerName, ... offerPhoto, userId })
.then(offer => res.json(offer))
.catch(next);
})
The uploadFile function worked with a simpler form where I just update a photo that is already there but cannot seem to get the image uploaded as part of the form.
I am very stuck and don't know what else to try.
A very big thanks to anybody who can help me in advance!
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}
/>
When I want to upload an image and send the other data using FormData. Axios seems to serialize the image. Therefore, when i upload the image using Axios, the image is inside the body's payload as a string. As a result, I'm unable to use Multer on the server side to retrieve the uploaded image from the request.
This is my Vue code:
export default () {
name: 'app',
data () {
image: ''
},
methods: {
onFileChange (e) {
var files = e.target.files || e.dataTransfer.files
if (!files.length) {
return
}
// console.log(files[0])
// var x = files[0]
return this.createImage(files[0])
// return new Buffer(x)
},
createImage (file) {
// var image = new Image()
var reader = new FileReader()
var vm = this
reader.onload = (e) => {
// vm.image = e.target.result
vm.image = file.toString('base64')
console.log(vm.image)
}
reader.readAsDataURL(file)
},
submitStaff () {
const formData = new FormData()
formData.append('file', this.image)
formData.append('name', this.data.name)
formData.append('username', this.data.username)
formData.append('password', this.data.password)
axios.post('api/myApiUrl', formData)
.then(function (response) {
console.log(response)
})
.catch(function (error) {
console.log(error)
})
}
}
<input type="file" #change="onFileChange">
Request Payload (Error with Vue)
Request Payload (Successful with Postman)
How do i fix this? Thank you!
I faced a problem related to it and in my case I was setting Content-Type as a default configuration for all my requests, doing this:
axios.defaults.headers.common['Content-Type'] = 'application/json [or something]';
And, as I remember, part of my solution was to remove this configuration to make the upload works. I just erased that line from my code and all requests worked well. As soon I removed it, I could notice that my headers request changed to this (see attached image and notice the Content-Type entry):
Another thing you should be alert is the field name to your <input type="file" name="file">. I have a java backend expecting a parameter named as "file", so, in my case, my input file HAS to be set as "file" on my HTML and on my server side, like this:
public ResponseEntity<Show> create(
#RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes) { ... }
Observe the entry #RequestParam("file")...
And in the HTML file:
<form enctype="multipart/form-data" novalidate v-if="isInitial || isSaving">
<div class="well">
<div class="form-group">
<label>* Selecione o arquivo excel. O sistema fará o upload automaticamente!</label>
<div class="dropbox">
<input type="file" single name="file" :disabled="isSaving" #change="filesChange($event.target.name, $event.target.files); fileCount = $event.target.files.length"
accept="application/vnd.ms-excel" class="input-file">
<p v-if="isInitial">
Solte o arquivo excel aqui<br> ou clique para buscar.
</p>
<p v-if="isSaving">
Carregando arquivo...
</p>
</div>
</div>
</div>
</form>
You also can take a look at this tutorial, it my help you: https://scotch.io/tutorials/how-to-handle-file-uploads-in-vue-2
I hope it helps you!