13

I have two functions: one that turn files into dataUrl and another that returns a promise with the result:

fileToDataURL(file) {
  var reader = new FileReader()
  return new Promise(function (resolve, reject) {
    reader.onload = function (event) {
      resolve(event.target.result)
    }
    reader.readAsDataURL(file)
  })
}  

getDataURLs (target) {
  // target => <input type="file" id="file">      
  return Promise.all(target.files.map(fileToDataURL))
}

target.files.map returns: TypeError: target.files.map is not a function. H

How to modify getDataUrls so it returns an array with the dataUrls?

alex
  • 7,111
  • 15
  • 50
  • 77

2 Answers2

30
function getDataURLs(target) {
  // target => <input type="file" id="file">      
  return Promise.all([...target.files].map(fileToDataURL))
}

FileList is not an Array, and does not inherit from Array, but it does implement the iterable protocol, so you can use the spread syntax to get it as an array.

In case you're wondering how to check if a class like FileList supports the spread syntax, you can do this:

console.log(FileList.prototype[Symbol.iterator]);

If that returns a function (which it does), then that returned function is a generator function that is invoked on an instance of the class by the spread syntax.

Patrick Roberts
  • 49,224
  • 10
  • 102
  • 153
  • Well I've provided links on things that may be unclear. Hopefully following them should clarify everything I'm covering. – Patrick Roberts Jun 13 '17 at 03:47
  • Why did you use `Promise.all` in your example? Why not using just `[...target.files].map` ? Is there any benefits using promises over array? – Mohamed Abdallah Jul 12 '21 at 10:25
  • @MohamedAbdallah `Promise.all()` takes an iterable of promises and returns a single promise which resolves to an array. Read the question, `Promise.all()` is used there as well, it's not just in my answer. FYI [`Promise.all()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) is extremely trivial to google. – Patrick Roberts Jul 12 '21 at 14:44
  • My question wasn't what is `Promise.all()`, it was what the benefit for that case over normal array. BTW thank you, your answer saved my day. – Mohamed Abdallah Jul 13 '21 at 22:22
4

While Patrick Roberts' answer is true, you may face an issue on TypeScript:

issue screenshot

Type 'FileList' is not an array type or a string type. Use compiler option '--downlevelIteration' to allow iterating of iterators. ts(2569)

You can find a complete answer about --downlevelIteration the post Why downlevelIteration is not on by default?.

In our case, to iterate on a FileList, instead of the spread operator, use Array.from():

function getDataURLs(target) {
  // target => <input type="file" id="file">      
  return Promise.all(Array.from(target.files).map(fileToDataURL))
}
aloisdg
  • 22,270
  • 6
  • 85
  • 105
  • There is a typo in the code - it has an extra `]` character. It should be: `return Promise.all(Array.from(target.files).map(fileToDataURL))` – Sators Mar 09 '22 at 19:05