I'm trying to upload multiple images in React (nextjs) from <input onChange={onChange} type='file' name='file' multiple/>
But which way is more proper to do it? Some hours of googling didn't helped me
This is my approach(that ofc doesn't work, otherwise i would not have to write it):
I stored images' srcs using useState hook:
const [imgsSrc, setImgsSrc] = useState([])
And this is my "input on change handler":
const onChange = (changeEvent: any) => {
for (let file of changeEvent.target.files) {
setTimeout(() => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => { setImgsSrc([...imgsSrc, reader.result]) }
reader.onerror = () => { console.log(reader.error)}
}, 1000)
}
}
My input one more time
<input onChange={onChange} type='file' name='file' multiple/>
And my way to trying to show images to user
{ imgsSrc.map((link) => { <img src={link}/> }) }
But it doesn't show me anything, so i have couple of questions that my nerve cells would be happy to get an answer to
why it doesn't work
does "saving images to public folder in root app directory" a good approach to save images
can i store base64 encode URLs remotely and get it every request, decode it and get an image
please...
You basically have a couple of mistakes that prevent your code from showing anything:
function App() {
const [imgsSrc, setImgsSrc] = useState([]);
const onChange = (e) => {
for (const file of e.target.files) {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
setImgsSrc((imgs) => [...imgs, reader.result]);
};
reader.onerror = () => {
console.log(reader.error);
};
}
};
console.log(imgsSrc, imgsSrc.length);
return (
<div>
<input onChange={onChange} type="file" name="file" multiple />
{imgsSrc.map((link) => (
<img src={link} />
))}
</div>
);
}
Since the file reader is asynchronous, you need to tell React to update the state using the most recent state, or you will have bugs, because setState is asynchronous as well, and the loop commands get batched and override the one another.
The fact that you did not have any image output is due to the fact that you are not returning anything from .map.
You can check the fixed version: https://stackblitz.com/edit/react-pgtpnu?file=src%2FApp.js
You can't save files on server if you are using NextJS and you deploy in a serverless environment, you need to use a cloud service, you could use firestore to save base64 strings, or even better, upload the blobs directly to cloudinary, through multer middleware nextJS serverless functions let you write such a function in a few lines.
Saving images in the public directory is not a good approach you should try cloud computing applications such as AWS, Google, or Cloudinary. but I would prefer using Cloudinary because you can store approximately 10GB of images in a free account and also it is easy to integrate with react applications.
How does it work?
1- save your image in Cloudinary through API request then Cloudinary returns the link of the image in response.
2-store the link of the image inside the database
tutorial => upload image cloudinary and react
Related
I am trying to include the option to upload an image in my app (React in the front side and NodeJS in the server one). The issue I am facing is that I am not able to send the file to the server. I saw other questions in this site and tutorials in other websites but I can't find what I'm doing wrong, so I hope you can, please, help me out.
This is part of my React code:
export default class DisplayPicture extends React.Component {
constructor(props) {
super(props);
// etc
}
onImageChange = (event) => {
if (event.target.files && event.target.files[0]) {
const img = event.target.files[0];
const formData = new FormData();
formData.append("image", img);
this.service
.uploadPicture(formData) // API call through a service
.then((backAnswer) => console.log(backAnswer))
.catch((err) => console.log(err));
}
};
render() {
return (
<div>
<p>Please, upload an image</p>
<input type="file" name="image" onChange={this.onImageChange} />
</div>
);
}
}
However, when I take a look at what the service sends to the server, it is an empty object and the server gets obviously the same in req.body and an "undefined" when looking for req.file.
I know the usual thing is to upload files by using a form, but I've already tried and got the same result, so I shared the shorter version.
Thanks in advance!!
My users should be able to upload their logo in my react APP,
Ideally what I want to do is upload the file somewhere IE host it and in the process of doing so, retrieve the URL for it so I can store that URL in the database along with the other settings!
My code for the user to drop the image is quite simple:
this.state = {
files: []
};
<DropZone
onDrop={(files, acceptedFiles, rejectedFiles) => {
this.setState({files: [...this.state.files, ...acceptedFiles]});
}}>
{uploadedFiles}
{fileUpload}
</DropZone>
Then when saving settings i want to call a function which as I said, Uploads the image somewhere and returns that URL so i can then use it to store in a DB.
saveDetails = () => {
// some code here that takes the this.state.files[0]
uploads it somewhere then returns the URL.. THEN i call a DB storage function storing that URL to then be accessed elsewhere?!
}
Is there a common, easy way to do this?
Thanks if you can help!
You want to use axios to post the image data. Use FileReader to read file. Read it as a base 64 image and send it to the server. To read file as a base 64:-
handleDrop(acceptedFiles){
const {onError,onChangeAvatar} = this.props;
const reader = new FileReader();
reader.onabort = ()=>onError("Profile picture reading is aborted!");
reader.onerror = ()=>onError("Error with uploaded file. Try again.");
reader.readAsDataURL(acceptedFiles[0]);
reader.onload = ()=>{
const base64Image = reader.result;
onChangeAvatar(base64Image);
};
}
Now you can use axios to post image:-
onChangeAvatar (base64Image){
Axios.post("/url/to?upload",base64Image).then(response=>console.log(response))
}
save the file and return saved url from server side.
I'm trying to learn more about working with files for an upcoming project. Is there a way with react-dropzone that I can run a function when onDrop happens to change the contents of the files I'm uploading? For example, if I'm uploading a word doc that says "Hi everybody" how would I change the content to "Hi Dr. Nick"?
I've tried just accessing the data by using the filereader api after before attempting to make the changes. I tried using filereader right after the opening curly bracket of the async function and wasn't able to get it to work.
import React from 'react';
import Dropzone from 'react-dropzone';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
const FileUpload = ({
children, disableClick, channelId, mutate, style = {},
}) => (
<Dropzone
style={style}
className="ignore"
onDrop={async ([file]) => {
const response = await mutate({
variables: {
channelId,
file,
},
});
console.log(response);
}}
disableClick={disableClick}
>
{children}
</Dropzone>
);
const createFileMessageMutation = gql`
mutation($channelId: Int!, $file: File) {
createMessage(channelId: $channelId, file: $file)
}
`;
export default graphql(createFileMessageMutation)(FileUpload);
Any thoughts on how to access and modify the contents of the file are greatly appreciated! Thanks.
onDrop={async ([file]) => {
var reader = new FileReader();
reader.onload = function(e) {
var contents = e.target.result;
displayContents(contents);
};
reader.readAsText(file);
}}
What you can do on frontend is to read file content and display it, all manipulations with file content should be done on server side. Push your file using ajax to your server with list of informations you want to change and use for example in nodeJS fs = require('fs')to make file modifications.
Also please visit this post i see that guys provided some solutions for your problem:
Is it possible to write data to file using only JavaScript?
and here is a fidlle taken from above link: http://jsfiddle.net/o4rhg1xe/1/
In Summary, you can read text file content using code i have provided and then use method from link to create Blob object with content you want and such file can be downlaoded by browser, (see fiddle)
Still in my opinion file modifications shoudl be moved to back-end side, ans i cant see no usecase to make them on frontend side.
I am using a plugin called dropzone which leaves rendering the image pretty much how to the user.
so I have done this
<Dropzone
onDrop={this.handleDropChange}}>
<p>
Try dropping some files here, or click to select files to upload.
</p>
</Dropzone>
#observable files = [];
#action
handleDropChange = (files) => {
files.forEach(file => {
this.loadFile(file);
});
}
#action
loadFile(file){
const reader = new FileReader();
reader.addEventListener('load',() => {
runInAction(() => {
this.files.push({file: file, preview: reader.result, uploaded: false})
window.URL.revokeObjectURL(reader.result);
});
},false);
// reader.readAsDataURL(file.fileObject);
}
<img src={this.props.preview} />
Problem is though when I drag in say 500 images it takes awhile for them to render on the screen. I think reactjs is having a hard time as it is essentially re-rendering 500 times since each "file reader load" causes a new item to be put into the files array.
Is there away to batch this or first do all the loading then re-render?
revokeObjectURL is for object url's and not meant for other strings|urls|base64.
You can create a objectURL by calling URL.createObjectURL like so:
var url = URL.createObjectURL(file || blob)
var image = new Image()
image.src = url
document.body.appendChild(image)
There is no need to use FileReader and read all the content encode it as base64 then let the browser decode it back into binary. that is a waste of resource...
revoking the objecturl should be done when you no longer need the url
I'm using p-fileUpload in Angular 6.0.0 app, for uploading the image files (In this case multiple images files). It has an event handler (uploadHandler) for the uploaded files.
<p-fileUpload multiple="true" name="myfile[]" accept="image/*" [auto]="true"
customUpload="true" chooseLabel="Select" (uploadHandler)="uploadImage($event)">
</p-fileUpload>
The files are getting uploaded correctly, but I'm not able to generate the base64 code when looping over the e.files array, because reader.onloadend is not firing. I've searched this firing issue and found some related questions but the solution of them is not solving my problem. Below is the code I've written for multiple image file upload -
uploadImage(e: any) {
if (e && e.files.length) {
const reader: FileReader = new FileReader();
e.files.forEach((i: File, j: any) => {
reader.onloadend = (ev: any) => {
console.log('inside??');
// Console is not triggered
};
// reader.readAsDataURL(...);
this.cdr.markForCheck(); // For outside zone detection
});
}
}
Please help me with this problem.
I'm using this same code for single image upload (so I don't need forEach loop there) inside the same app and it is working in all other places unlike in this case.