Live Image Previews for File Uploads using the FileReader API
March 29, 2018
JavaScript
It’s a common practice today for users to contribute content by uploading images. A typical scenario is a user-submitted image for a profile photo selected using the <input>
form element. Users have come to expect that when they select an image for uploading, they’ll be able to preview the image before submitting it.
By default, when a user selects an image using the file <input>
element, its reference is temporarily stored as a path; it needs to be read and processed before it can be shown onscreen.
We want users to be able to preview the image that they’ve selected before it’s sent to a database or API endpoint. To accomplish this, we’ll make use of the FileReader
object, which is part of the File API.
Basic Setup
Input and Image Elements
Our basic structure will be an image container with an empty <img>
element and an <input>
element with a type of file
. To limit the file uploads to common image MIME types, we can use the accept
attribute on the <input>
element and pass in a list of acceptable file extensions. Lastly, we’ll attach data attributes for JavaScript DOM targeting and a class for our image container.
<div class="Preview-img">
<img data-id="filePreview" />
</div>
<input type="file" accept=".jpg, .jpeg, .png, .gif" data-id="fileInput" />
Providing a few Basic Display Constraints with CSS
To ensure that our images fit within the confines of our layout, we’ll use the object-fit
property and set it to cover
. This will crop all images to fit within its parent container, regardless of size or aspect ratio.
.Preview-img {
background-color: gainsboro;
margin-bottom: 1rem;
width: 300px;
height: 200px;
& img {
object-fit: cover;
object-position: center;
width: 100%;
height: 100%;
}
}
Accessing the Elements in JavaScript
To handle when a user selects an image for upload, we’ll attach an event listener to the <input>
element with the 'change'
event. When a user selects a file, a reference to that file will be added to the files
array on the input
object. Each file element in this array holds information on the file, including filesize, MIME type, date modified, etc.
For now, we’ll log the files attached to the input element.
const filePreview = document.querySelector('img[data-id=filePreview]')
const fileInput = document.querySelector('input[data-id=fileInput]')
fileInput.addEventListener('change', handleFileUpload, false)
function handleFileUpload(inputEvent) {
const input = inputEvent.target
console.log(this.files)
}
Using the FileReader Object
If we take a look at the log output after choosing an image file at this point, we’ll see that this.files
contains a FileList
object with an array of File
Blob
objects (in this case, we’ll only have one File
). To read the data that the File
represents and serve a preview of it, we’ll need to use the FileReader
object.
Loading the Image Preview
The FileReader
constructor returns a new FileReader
object, which has several useful methods and events for handling file data. First, we’ll use the readAsDataURL()
method to read the content of the File
object. The readAsDataURL()
method will take in our File
object as a Blob
and return a result as a DataURL
. Next, we’ll hook into the onload
event, which is fired when a read operation is successfully completed. When onload
is fired, we’ll set the src
attribute of our <img>
element to the FileReader result, which is the image DataURL
.
Voila! Now we can preview our image as soon as we select it. 🙌
function handleFileUpload(inputEvent) {
const input = inputEvent.target
const reader = new FileReader()
reader.readAsDataURL(input.files[0])
reader.onload = (readerEvent) => {
filePreview.src = readerEvent.target.result
}
}
Note that we’re dealing with two different events here: the first event is the
<input>
‘change’ event, while the second is theFileReader
‘load’ event.
Image Type Validation
Setting the accept
attribute on the file <input>
element will limit the types of files that users can choose, but there are ways to circumvent this restriction. If the uploaded files are going to be sent to a database, we want to ensure that we are sending the right file type. To provide a second level of validation we can perform a MIME type check at the time the file is handled.
The File
object attached to the <input>
element has a type
property with a String
value corresponding to the file’s MIME type. When we handle the file, we can check this property against a list of valid MIME types and only continue processing the file if it is in an accepted format. To do this, we’ll define an array of MIME type strings and check if the type
value matches an array element.
const filePreview = document.querySelector('img[data-id=filePreview]')
const fileInput = document.querySelector('input[data-id=fileInput]')
const validImgFormats = ['image/jpeg', 'image/png', 'image/gif']
fileInput.addEventListener('change', handleFileUpload, false)
function handleFileUpload(inputEvent) {
const input = inputEvent.target
const reader = new FileReader()
if (validImgFormats.indexOf(input.files[0].type) === -1) {
return alert(
'Please provide a valid image file. Accepted formats include .png, .jpg, and .gif.'
)
}
reader.readAsDataURL(input.files[0])
reader.onload = (readerEvent) => {
filePreview.src = readerEvent.target.result
}
}
Wrapping Up
We can now show a live preview of images that are selected with file <input>
form elements. What if we wanted to allow multiple simoultaneous image uploads along with a preview for each image? That’s a simple as adding the multiple
attribute to the <input>
element and iterating through the returned FileList
.
Interactive Demo
You can view an interactive demo on CodePen.