How to Preview Uploaded Image File From File Input with Reactjs? - javascript

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

Related

uploading multiple images and saving them to localStorage in javascript

I am trying to make a feature for my web application that allows the user to upload multiple images as a part of a blog post, then view them later on a page that shows a list of all blog posts. What I am trying to do is, allow the user to upload multiple images and save those images to localStorage. Currently I am looping through all the image files the user has uploaded, then adding a new fileReader to each one. In order to add them to the localStorage, I know I have to make the key value of each image different (i.e. img1=the first image, img2=the second image, etc.) As per the code below, I am changing the key value for each image uploaded using a for loop, and setting it to img${i}.
However when I try to add a "load" event listener onto each fileReader component to read each individual image file in order to save it onto localStorage, because it is a "load" event the value of the "i" is different. So as a result, I can only save the final image that has been uploaded (i.e. img3). I am assuming this is caused by the code that is within a "load" event so that it will only consider the final "i" value when all the files have loaded. Would there be a way around this issue? Any help towards the right direction is greatly appreciated. I am trying to achieve this in pure javascript so no jquery please. Thank you.
HTML
<!--Upload images-->
<input type="file" id="filetag" multiple accept=".jpg,.jpeg,.png,.gif">
<img id="preview">
Javascript
const fileSelector=document.getElementById('filetag');
const display = document.getElementById("preview");
fileSelector.addEventListener('change',(e)=>{
//gets all the files uploaded
let fileList = e.target.files;
console.log(fileList)
for(i=0;i<fileList.length;i++){
//add a new fileReader for each element
let reader = new FileReader();
reader.addEventListener("load", () =>{
//store each uploaded image into localStorage
localStorage.setItem(`img${i}`,reader.result)
console.log(i)
})
reader.readAsDataURL(fileList[i]);
}
})
First, I don't think that you should use LocalStorage for this. Besides LocalStorage size is limited. In case you have a back-end, those images should be saved in the server, and then just load them with the URL, like any normal website.
In any case, I answer your question. Instead of using an array (and having that problem with the index), turn it into an object and then use the object key:
let fileList = e.target.files;
let files = {};
let index = 0;
for(let file of files) {
files[index] = file;
index ++;
}
// Now iterate object instead of array
Object.keys(files).forEach( key => {
let reader = new FileReader();
reader.addEventListener("load", () =>{
localStorage.setItem(`img${key}`,reader.result)
})
reader.readAsDataURL(files[key]);
});

Host image and use URL

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.

How to read or convert an image blob url into an image?

I have an image that is stored in local storage. On load the image is displayed.
componentDidMount() {
const reader = new FileReader();
reader.onload = e => {
this.setState({ blob: e.target.result });
};
reader.readAsDataURL(this.props.file);
}
render() {
return (
<div>
<img src={this.state.blob} />
</div>
)
}
The file object looks like this:
lastModified:1535424554987
name:"test.jpeg"
preview:"blob:http://localhost:8080/b52098ca-087f83c778f0"
size:41698
type:"image/jpeg"
webkitRelativePath:""
When I refresh the page and try to load the preview again, it doesn't display the image, and the file object looks different:
{preview: "blob:http://localhost:8080/b52098ca-087f83c778f0"}
Is it possible to read the image from this url? Or is it impossible to render the image without the full file object?
blob URL is temporary. if you reload the web page, the blob URL will gone.
If you want to save the image in localStorage you can save it as base64 data.
But if your image is too big localStorage won't suit because it has limits which depend on the browser from 5MB to 10MB.
It's impossible as far as i'm aware to save files to/from localStorage this is because it runs JSON.stringify over the data.

FileReader.readAsDataURL() reader result is empty for font files such as .ttf, .woff, and .woff2

I am building a web interface that allows the user to upload a custom font. The problem is that I am unable to convert the font files (specifically, .ttf, .woff, and .woff2 files) to base64 via the FileReader.readAsDataUrl() so that I can then PUT these files via my API.
I can successfully reasAsDataUrl .png and .jpg files, and receive those files' data as a base64 encoded strings, no problem. However, when I try to do the same thing with a .ttf, .woff, or .woff2 file, reader.result is empty although the FileReader doesn't throw any error.
I am using input elements to allow the user to select a font:
<input type="file" accept="application/font-woff" onChange={handleUpload}/>
The handleUpload function looks like this:
handleUpload: () => (event: Object) => {
const { currentTarget: element } = event;
if (!element.files) { return; }
const file = element.files[0];
const reader = new FileReader();
reader.onerror = (error) => {
console.error('Error: ', error);
};
reader.readAsDataURL(file);
console.log(reader.result); // reader.result is empty
}
This is the file object that I am passing to the readAsDataURL function.
I figured it out :-)
readAsDataURL was working successfully but I was returning reader.result before the read was complete. I changed my code to include
reader.addEventListener('load', () => {
change(name, reader.result);
}, false);
and now it's working!
Additional info (edit): The reason why it was working for the image files was because in the onUpload function for those input components I was rendering a preview image which depended on the file being done loading... as a side effect of waiting for the file in order to render the preview, I was also ensuring that the file was loaded before I did anything with it afterwards. I was happy to realize this because if one day, for some reason, I removed the image preview piece, my image upload would have also broken!

How to load an HTML5 Canvas with an image from an image-service?

My web app calls a Web API service, which returns an image. The service returns nothing but an image. Calling the service is little different because there is a function in the routing code that adds the required auth-code and such. Anyway, my point is, I don't have the full URL and even if I did, I wouldn't want to pass it into code in plain-text. So what I have is a response, and that response is an image.
getThumb(filename: string) {
return this.http.get('/Picture/' + filename).subscribe(response => {
return response;
});
}
What I need to do is draw that image on to a canvas. From what I've seen on the internet so far, it looks like I want to create an image element, then assign that element src a URL, then I can add it to the canvas. It's the src part that's perplexing me. All the samples I see are either loading the image from a local filesystem or predefined URL, or from a base64 string, etc. I can't figure out how to just load an image I have as a response from a service. I'm sure I'm overthinking it.
Does anyone have some sample code to illustrate this?
e.g Something like this:
var img = new Image(); // Create new img element
img.src = ... ; // Set source to image
You could convert the image to Base64. In my example, you request the image and convert it to a blob using response.blob(). Once it's a blob, use fileReader.readAsDataURL to get the Base64.
const fileReader = new FileReader();
fetch("image-resource").then((response) => {
if(response.ok) {
return response.blob();
}
}).then((blob) => {
fileReader.readAsDataURL(blob);
fileReader.onloadend = () => {
console.log(fileReader.result);
}
});
References:
readAsDataURL
Blob

Categories