Angular 2/4 FileReader Service - javascript

I was trying to create Angular 2/4 Service with possibily to upload files. I could not find solution on any resourse so I probably wanna ask you guys. So the idea is somewhere in Component there is input field with type=file. It has directive (change)="uploadFile($event)".
In component .ts file:
uploadFile(event) {
this.images.push(this.uploadImgService.uploadImage(event));
}
UploadImgService looks this way:
private img: string;
uploadImage(e) {
const file = e.target.files[0];
const pattern = /image-*/;
if (!file.type.match(pattern)) {
alert('You are trying to upload not Image. Please choose image.');
return;
}
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onloadend = () => {
this.img = reader.result;
};
return this.img;
}
So, I understand that operation is going async, but I can't figure out how to wrap it the way it could wait until img is load. I think it is the result of skill lack:(
When I post this code into component it surely works, but my idea is to make service.
Also, I'm just a beginner in Angular. So, if there is a better way to reilize this idea I would be glad to hear from you. Thanks!

You should return an observable like so:
uploadImage(e) {
const file = e.target.files[0];
const pattern = /image-*/;
if (!file.type.match(pattern)) {
alert('You are trying to upload not Image. Please choose image.');
return;
}
const reader = new FileReader();
reader.readAsDataURL(file);
return Observable.create(observer => {
reader.onloadend = () => {
observer.next(reader.result);
observer.complete();
};
});
}
And in the component, subscribe to the observable:
this.service.uploadImage(event).subscribe((img) => {
// Do what you want with the image here
});

Related

How to use FileReader.readAsText() synchronously?

I am trying to read a CSV file using FileReader.readAsText() in JavaScript. But I am not able to get the value synchronously. I tried multiple approaches. But none is working. Below is the code I have so far:
1st Approach:
<input
id = "inputfile"
type = "file"
name = "inputfile"
onChange = {uploadFile} >
const uploadFile = (event: React.ChangeEvent < HTMLInputElement > ) => {
let resultSyncOutput = '';
connst files = event?.target.files;
if (files && files.length > 0) {
readAsTextCust(files[0]).then(resultStr => {
resultSyncOutput = resultStr;
});
}
// At this line I am not able to get the value of resultSyncOutput with the content of file sychronously
//Do something with the result of reading file.
someMethod(resultSyncOutput);
}
async function readAsTextCust(file) {
let resultStr = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.onload = (e) => resolve(fileReader.result);
fileReader.readAsText(file);
});
console.log(resultStr);
return resultStr;
}
This is the first approach I tried ie, using async/await. I also tried to do it without aysnc/await and still was not able to succeed. It is critical for this operation to be synchronous.
NB: I checked a lot of answers in Stack Overflow and none provides a solution to this problem. So please do not mark this as duplicate.
Answer to this particualr question is not provided anywhere

How to read file using FileReader.readAsText synchronously in JavaScript?

I am trying to read a CSV file using FileReader.readAsText() in JavaScript. But I am not able to get the value synchronously. I tried multiple approaches. But none is working. Below is the code I have so far:
1st Approach:
<input
id = "inputfile"
type = "file"
name = "inputfile"
onChange = {uploadFile} >
const uploadFile = (event: React.ChangeEvent < HTMLInputElement > ) => {
let resultSyncOutput = '';
connst files = event?.target.files;
if (files && files.length > 0) {
readAsTextCust(files[0]).then(resultStr => {
resultSyncOutput = resultStr;
});
}
// At this line I am not able to get the value of resultSyncOutput with the content of file sychronously
//Do something with the result of reading file.
someMethod(resultSyncOutput);
}
async function readAsTextCust(file) {
let resultStr = await new Promise((resolve) => {
let fileReader = new FileReader();
fileReader.onload = (e) => resolve(fileReader.result);
fileReader.readAsText(file);
});
console.log(resultStr);
return resultStr;
}
This is the first approach I tried ie, using async/await. I also tried to do it without aysnc/await and still was not able to succeed. It is critical for this operation to be synchronous. Also use of Ajax is not allowed in project.
NB: I checked a lot of answers in Stack Overflow and none provides a solution to this problem. So please do not mark this as duplicate. Answer to this particular question is not provided anywhere.
Please help me even if this looks simple to you
I found the solution resultSyncOutput = await readAsTextCust(files[0]); and declaring the calling function as async worked.
You need to set a callback function for the onload event callback to get the results. Also notice that you need to call readAsText on the uploaded CSV file.
You can use FileReader API inside input's onChange callback this way:
<input
id = "inputfile"
type = "file"
name = "inputfile"
onChange = function (event: React.ChangeEvent<HTMLInputElement>) {
const reader = new FileReader();
reader.onload = (e) => {
console.log(e.target?.result) // this is the result string.
};
reader.readAsText(event.target.files?.[0] as File);
};
No need for async/await as well.

How to get an image uploaded and store it in the firebase storage?

I have the following upload button.
<input type="file" id="logo-image-upload" onChange="imageValidation()">
The following function gets called whenever an image is uploaded.
function imageValidation(){
let storageRef = firebase.storage.ref('user-logos')
let imageInput = document.getElementById('logo-image-upload')
if(imageInput.files && imageInput.files[0]){
let reader;
reader = new FileReader();
reader.onload = (e) => {
storageRef.put(//Blob).then(function(snapshot) {
console.log("Image Uploaded")
});
}
}
}
What can I pass as the value of the Image so I can upload it to the firebase storage?
Please add your html code as well for better understanding.
But if your input is of type File you can directly upload it to the firebase.
There is no need to convert it in Blob.
You can refer the below code for achieving your goal.
let uploadTask: firebase.storage.UploadTask = storageRef.put(imageInput.files[0]);
uploadTask.on(firebase.storage.TaskEvent.STATE_CHANGED,
(snapshot: any) => {
let x: number = (snapshot.bytesTransferred / snapshot.totalBytes) * 100;
return subject.next({ status: x, fileUploadSuccess: false, fileUploading: true, fileUploadFailed: false, progress: true });
})
I think you can resolve your issue by using this approach.

preventDefault is not a function error when trying to pass an image

I'm using an image cropper to crop an image. I'm using the following library
https://github.com/mosch/react-avatar-editor
I have implemented this correctly and i'm getting the cropped image using this function.
handleSave = () => {
const img = this.editor.getImageScaledToCanvas().toDataURL();
this.props.croppedImage(img);
};
Here i have passed the cropped image using props to another function as follows
this.props.croppedImage(img);
This is where i have implemented the cropper (2nd component)
<CropperTest croppedImage={this.getCroppedImg}/>
In the 2nd component i have used this function to access the cropped image and again pass that image to another function. This is where i get the issue.
getCroppedImg(img) {
this.props.onImageImgPoll(img);
}
onImageImgPoll function
onImageImgPoll(event) {
event.preventDefault();
let array = this.state.filepoll.slice();
let unq_file = event.target.files[0];
if (event.target.files && event.target.files[0]) {
let reader = new FileReader();
reader.onloadend = (e) => {
array.push(unq_file);
};
reader.readAsDataURL(unq_file);
this.setState({
image: event.target.result,
filepoll: array,
filestar: []
});
}
};
When running this code i'm getting this error
event.preventDefault is not a function
What I've found out is the cropped image is in base64 format. Maybe that might be the issue. How can i solve this?
Make sure you have added this.onImageImgPoll= this.onImageImgPoll.bind(this); in your component constructor

Javascript testing with mocha the html5 file api?

I have simple image uploader in a website and a javascript function which uses FileReader and converts image to base64 to display it for user without uploading it to actual server.
function generateThumb(file) {
var fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = function (e) {
// Converts the image to base64
file.dataUrl = e.target.result;
};
}
Now I am trying to write tests for this method using Mocha and Chai. My thinking is that I want to check if the file.dataUrl was successfully created and it is base64. So I would like to mock the local file somehow in testing environment (not sure how to do this). Or I should not test this at all and assume that this is working ?
The answer here depends a little. Does your "generateThumbs" method have other logic than loading a files contents and assign that to a property of a passed in object? Or does it have other logic such as generating the thumbnail from the image data, reading out file properties and assigning them to the file object? and so on?
If so then I would infact suggest you mock out the FileReader object with your own, so that you can control your test results. However, it isn't the FileReaders functionality you want to test, it is your own logic. So you should assume that FileReader works and test that your code that depends upon it works.
Now since the method you posted was a bit on the small side, In that case I would just assume it works, rename the method and work on testing the rest of your code. But there is a place for having such a mock, and I must admit it was quite fun to figure out how to mock the event target, so I will give an example here, using your method as a basis:
//This implements the EventTarget interface
//and let's us control when, where and what triggers events
//and what they return
//it takes in a spy and some fake return data
var FakeFileReader = function(spy, fakeData) {
this.listeners = {};
this.fakeData = fakeData;
this.spy = spy;
this.addEventListener('load', function () {
this.spy.loaded = true;
});
};
//Fake version of the method we depend upon
FakeFileReader.prototype.readAsDataURL = function(file){
this.spy.calledReadAsDataURL = true;
this.spy.file = file;
this.result = this.fakeData;
this.dispatchEvent({type:'load'}); //assume file is loaded, and send event
};
FakeFileReader.prototype.listeners = null;
FakeFileReader.prototype.addEventListener = function(type, callback) {
if(!(type in this.listeners)) {
this.listeners[type] = [];
}
this.listeners[type].push(callback);
};
FakeFileReader.prototype.removeEventListener = function(type, callback) {
if(!(type in this.listeners)) {
return;
}
var stack = this.listeners[type];
for(var i = 0, l = stack.length; i < l; i++) {
if(stack[i] === callback){
stack.splice(i, 1);
return this.removeEventListener(type, callback);
}
}
};
FakeFileReader.prototype.dispatchEvent = function(event) {
if(!(event.type in this.listeners)) {
return;
}
var stack = this.listeners[event.type];
event.target = this;
for(var i = 0, l = stack.length; i < l; i++) {
stack[i].call(this, event);
}
};
// Your method
function generateThumb(file, reader){
reader.readAsDataURL(file);
reader.addEventListener('load', function (e) {
file.dataUrl = base64(e.target.result);
});
}
Now we have a nice little object that behaves like a FileReader, but we control it's behavior, and we can now use our method and inject it into, thus enabling us to use this mock for testing and the real thing for production.
So we can now write nice unit tests to test this out:
I assume you have mocha and chai setup
describe('The generateThumb function', function () {
var file = { src: 'image.file'};
var readerSpy = {};
var testData = 'TESTDATA';
var reader = new FakeFileReader(readerSpy, testData);
it('should call the readAsDataURL function when given a file name and a FileReader', function () {
generateThumb(file, reader);
expect(readerSpy.calledReadAsDataURL).to.be.true;
expect(readerSpy.loaded).to.be.true;
});
it('should load the file and convert the data to base64', function () {
var expectedData = 'VEVTVERBVEE=';
generateThumb(file, reader);
expect(readerSpy.file.src).to.equal(file.src);
expect(readerSpy.file.dataUrl).to.equal(expectedData);
});
});
Here is a working JSFiddle example:
https://jsfiddle.net/workingClassHacker/jL4xpwwv/2/
The benefits here are you can create several versions of this mock:
what happens when correct data is given?
what happens when incorrect data is given?
what happens when callback is never called?
what happens when too large of a file is put in?
what happens when a certain mime type is put in?
You can offcourse massively simplify the mock if all you need is that one event:
function FakeFileReader(spy, testdata){
return {
readAsDataURL:function (file) {
spy.file = file;
spy.calledReadAsDataURL = true;
spy.loaded = true;
this.target = {result: testdata};
this.onload(this);
}
};
}
function generateThumb(file, reader){
reader.onload = function (e) {
file.dataUrl = base64(e.target.result);
};
reader.readAsDataURL(file);
}
Which is how I would actually do this. And create several of these for different purposes.
Simple version:
https://jsfiddle.net/workingClassHacker/7g44h9fj/3/

Categories

Resources