0

I have a Drag and Drop event that is connected to my imageDrop function. I am trying to get the dataurl into an array so that I can display images via a slider view or a grid view. I am able to get the name of the file and the type I just need help figuring out how to get it to display the image.

I have tried setting up the reader.onload but when i run the page it does not output the expected values

  imageDrop(e) {
        e.stopPropagation();
        e.preventDefault();
        let dt = e.dataTransfer;
        for(var i = 0, n= dt.files.length; i < n; i++){
            var file = dt.files[i];
            var reader = new FileReader();
            reader.onload = (function(f){
                return function(e){
                    e.target.result;
                }
            });(file)
            this.imageFiles.push({
                name: dt.files[i].name,
                data: reader.readAsDataURL(file),
                type: dt.files[i].type
            });
        }
   }

What I am expecting is to get the name of the file, the type and the dataurl to display the image into the imageFiles array.

DRW
  • 335
  • 1
  • 3
  • 17
  • I'm afraid that isn't possible [How to get path directory from FileReader()?](https://stackoverflow.com/questions/53759070/how-to-get-path-directory-from-filereader) – Emiel Zuurbier Aug 21 '19 at 20:51
  • Not trying to get the path , trying to put the base64 encode into an array so i can then use the repeat function of lit-element to render the image out to the browser. I never said anything about getting the file path . – DRW Aug 21 '19 at 22:33
  • 1
    Note that you absolutely don't need the FileReader here, nor any asynchronous scripting: `imageDrop(e) { e.stopPropagation(); e.preventDefault(); this.imageFiles.push.apply(this.imageFiles, ( [...e.dataTransfer.files].map( (f) => ({ name: f.name, data: URL.createObjectURL(f), type: f.type }) ) ) ); }` is all you need: https://jsfiddle.net/8va0r4tw/ – Kaiido Aug 23 '19 at 06:31

1 Answers1

0

The problem lies in your onload callback function and the array push that follows. The push to the array occurs first and so the values are added to the array without the data property, because at that time, there is no value. That value can be retrieved from within the onload callback.

You'll want to have the image added to the array whenever the image has been decoded into a base64 string.

You can do that by moving the this.imageFiles.push into the load event callback. When the load event is called the reader.result value has been updated and now contains the base64 string you require.

I assume that this is a method of a class you are writing because you are referring to this. So I've used an arrow function to make this refer to the context of the class.

imageDrop(e) {
    e.stopPropagation();
    e.preventDefault();
    let dt = e.dataTransfer;
    for(var i = 0, n = dt.files.length; i < n; i++){
        const file = dt.files[i];
        const reader = new FileReader();
        reader.addEventListener('load', () => {
            this.imageFiles.push({
                name: file.name,
                data: reader.result,
                type: file.type
            });
        }, {once: true});
        reader.readAsDataURL(file)
    }
}

Try it out and I hope it fixes your problem.

Emiel Zuurbier
  • 19,095
  • 3
  • 17
  • 32
  • With some minor changes it does work as expected I had to change the dt.files.name to file.name and same with type and it all worked out. Thank you for your help – DRW Aug 22 '19 at 17:29
  • You are welcome. I've added your minor change to the answer. – Emiel Zuurbier Aug 22 '19 at 17:37
  • I did some more testing, I am able to process one image at a time, but when I do multiple images it only collects data from the last file that was dropped and processes it the number of times as files that I dropped in . sometimes I get 2 sometimes I get one but it is all the same data 0: {name: "ashes-of-creation-title copy.jpg", data: "", type: "image/jpeg"} 1: {name: "ashes-of-creation-title copy.jpg", data: "data:image/jpeg;base64,/9j/4RTIRXhpZgAATU0AKgAAAAg..., 2: {name: "ashes-of-creation-title copy.jpg", data: "data:image/jpeg;base64,/9j/4RTIRXhpZgAATU0AKgAAAA... – DRW Aug 23 '19 at 01:17
  • I've added a closure to the `load` event handler. Try it out and let me know if it works. – Emiel Zuurbier Aug 23 '19 at 06:16
  • Using `const file = dt.files[i];` instead of `var` might also solve the problem. – Emiel Zuurbier Aug 23 '19 at 06:43
  • had to make some minor changes again, had to add t as a parameter then this with the file, reader and then the array to t.imageFiles and it all works correctly I get all the images that are dropped and the data .. thanks again for your help .. – DRW Aug 23 '19 at 17:10
  • Great to hear. I realized that the closure changed the context of `this` and tried another approach which uses `const` and should have the same result. Answer is based on [this](https://decembersoft.com/posts/understanding-javascript-closures-in-for-loops/) article. – Emiel Zuurbier Aug 23 '19 at 17:13