In the last couple of years, modern browsers have been able to surprise us with many fantastic new features, and it is incredibly hard to keep up to date with all of these new features. I was personally not aware that all major browsers now have access to a powerful Web API object: FileReader. Today I am going to share with you my experience of this browser feature.
The FileReader allows user to read file content, directly in the browser, without the need for the file to be uploaded to the server. This feature has been introduced to provide a better user experience, and it should NOT replace server side validation.
When I first discovered this feature I was very excited but could not think of any specific user cases, until I started to play with it. Since then I was able to use it in the following user cases:
-
Preview an image before uploading (thumbnail)
-
Read file content to return a quick validation to the user ( For example: read a specific string or regex)
-
Provide support for offline browsing
-
Convert file to data URL
-
Provide Client side, Document creation
Today we are going to build a Text editor, directly in the browser. A detailed walk through of all the code necessary will be provided throughout the article, but an understanding of Javascript is required to follow the article.
The finished work will provide the following features:
-
Ability to upload a text file
-
Ability to modify the content of the text file
-
Ability to download the modified text file
Browser Support
As mentioned above, the FileReader support is quite extensive as it is now fully supported on all major browsers, with partial support going back to IE10 as shown by the table provided by caniuse.com.
FileReader in Action
In the following chapter, we are going to build our first FileReader implementation. The first step involves the initialization of the file reader object using the object constructor function syntax. This will create an instance of the file reader Web API:
const reader = new FileReader();
Now that our reader is initialized, we are able to attach to some of its events handlers (Events handlers list). We are going to cover this event in more detail later in the post.
reader.onload = MyLoadFunction
reader.onerror = MyErrorFunction
This can also be achieved by using the addEventListener syntax (Events list):
reader.addEventListener("load", myLoadFunction);
reader.addEventListener("error", myErrorFunction);
It is now time to use the readAsText method provided by the API (list of methods). The aim of this method is to read the content of the file provided. Throughout the process the FileReader API is going to trigger events that will trigger the relevant callbacks (myLoadFunction, myErrorFunction). The reader methods accept one argument that is expected to be a file or a Blob object.
reader.readAsText(_file);
Finally, we need to declare our _file variable by taking advantage of the File API. Creating a file in Javascript from scratch is not always required, as the FileReader can access files from multiple sources (File Constructor, input of type file, drag and drop, Canvas ,etc..)..
const _file = new File(
["My newly created text file"], //fileContent
"foo.txt", //fileName
{ type: "text/plain" } //fileType
);
If successful, our code above is going to trigger our MyLoadFunction event callback.This method has access to the reader result and state as part of its arguments. A successful callback from a readAsText method will include the following properties:
{
...,
readyState: 2, // 0 = EMPTY, 1 = LOADING, 2 = DONE
Result: "My text file content"
}
The table below is going to show all the different events associated state and Value. The code required to produce the following table can be accessed on this codepen: https://codepen.io/zelig880/pen/rEVEpK
FileReader event | |||
Scenario | EventName | State | Result |
Simple upload | on initialization (new FileReader()) | 0 (Empty) | empty |
onloadstart | 1 (Loading) | empty | |
onprogress | 1 (Loading) | My newly created text file | |
onload | 2 (Done) | My newly created text file | |
onloadend | 2 (Done) | My newly created text file | |
Abort | on initialization (new FileReader()) | 0 (Empty) | empty |
onloadstart | 1 (Loading) | empty | |
onabort | 2 (Done) | empty | |
onloadend | 2 (Done) | empty |
Our first implementation of the file reader is completed and the code can be accessed on the following codepen: https://codepen.io/zelig880/pen/EBYwer
Browser File Editor
In this chapter we are going to expand what we have written so far and turn it into a web browser text file editor.
To achieve our goal, we will have to make the following modifications to our existing code, by taking advantage of different methods and feature offered by the API.
-
Change the FileReader output to be of type Data URL
-
Use the Data URL to provide a download functionality
-
Provide input to the client to update some text
-
Use the FileReader to read a file uploaded using an input of type file
-
Dynamically update the text field with the uploaded file content
Change the FileReader output to be of type Data URL
Our first step requires us to change the method used to read our file. The readAsText is going to be replaced with readAsDataUrl. This method is going to provide us with a DATA URL ( a url with a base64 representation of our file).
reader.readAsDataURL(_file);
Use the Data URL to provide a download functionality
A Data URL on its own is not very useful. So in this section we are going to provide a very simple functionality to allow the download of the newly created file.
To achieve our download, we are going to use an anchor element . The element will have our Data URL dynamically set as its href and an attribute of download. This attribute specifies that the target file will be downloaded when the user clicks the link instead of navigating to the file.
<a href="#" id="fileDownload" download>Newly Generated File</a>
To dynamically set our href, we are going to utilize the onload event triggered by the FileReader, by declaring a callback function:
reader.onload = (e) => {
const fileDownload = document.getElementById("fileDownload");
fileDownload.href = e.target.result;
}
Our text editor is starting to take shape. At this point we are able to download a "static" text file.
Provide input to the client to update some text
It is now time to provide some control to our users, and make things more dynamic. To carry out this task, we are going to create a simple text input, used to dynamically modify the content of our file.
The Javascript code will look like this:
//create a file with dynamic content
function createFileObj(content, name){
const file = new File(
[content],
`${name}.txt`,
{ type: "text/plain" }
);
return file;
}
//fetch the value from the input
const textInput = document.getElementById("textInput");
const inputValue = textInput.value;
//The _file variable will be used when calling our FileReader
const _file = createFileObj(inputValue, "My Text file");
The code above has made our text editor come to life. We are now able to modify the text or our downloaded file.
Use the FileReader to read a file uploaded using an input of type file
Finally, to expand our knowledge of the FileReader API, we are going to enhance our editor with another feature. We are going to give the user the ability to upload a text file from their computer and modify it with the code written above.
To accomplish this, we will create an input of type file, and listen to the change event triggered by this element to initialize our FileReader ( this is the most common use of the file reader api).
//listen to the change event triggered by the input element
const fileInput = document.getElementById("fileUploadInput");
fileInput.addEventListener("change", handleUpload);
function handleUpload (event){
//fetch the first file (the element could provide multiple files)
var file = event.target.files[0];
var reader = new FileReader();
//use the textInput variable previously declared to update the text input
reader.onload = (e) => { textInput.value = e.target.result };
//trigger the fileReader
reader.readAsText(file);
}
This codepen includes our fully functional code: https://codepen.io/zelig880/pen/XLraXj
Conclusion
It is incredible how much can be achieved nowadays with javascript directly within the browser.
In less than 50 lines, we have been able to write a fully functional text editor, it may not be production ready (it has a couple of bugs), but the thought that we can achieve so much, with so little code, fills me with excitement.
TLDR:
The complete code for our text editor can be found at the following codepen: https://codepen.io/zelig880/pen/XLraXj
This post was written by Simone Cuomo who is a mentor and senior software engineer at ThisDot.
You can follow them on Twitter at @zelig880.