Understanding FileReader
The FileReader API is event driven. You define custom behavior in its event handlers when it either succeeds or fails to read a file and then pass a File object to one of the methods, such as readAsText
The result of the method, if successful, can finally be retrieved in the .result
property.
The FileReader methods work asynchronously but don't return a Promise. And attempting to retrieve the result immediately after calling a method will not work, as the .onload
event handler fires only after the FileReader has successfully finished reading the file and updates the FileReader's .result
property. This can make FileReader a bit of a pain to work with when integrating it with an application.
Wrapping it With a Promise
A solution to make FileReader more pleasant to work with is to wrap its result in a Promise. Here's an example:
This function returns a Promise of a string that will resolve or reject only after the firing of the .onload
and .onerror
event handlers, respectively. If the Promise is rejected the FileReader will abort and, in this case, return a custom DOMException but you can reject
with whatever you like. One good alternative is the FileReader's .error
property, which will also be a DOMException.
A Simplified API with Async/Await
Wrapping the result of a FileReader in a Promise allows us to await
the result of our readUploadedFileAsText
function and expect a string. Here's an example onchange
handler we could pass to a <input type="file />
element (Full example on CodePen)
React Examples
The CodePen link above shows the above code doing manual DOM manipulation. Here's an example of using the same readUploadedFileAsText function with async/await in a React application.
For an example in a TypeScript and React application, look here. Though this particular code example is part of an application using Redux so there's a fair bit of indirection.