On the internet today, file upload is an extremely used feature in modern web applications. On a daily basis, people exchange millions of files through the internet, moving files from one server to another. Developers have therefore created a variety of techniques to quickly add file upload functionalities. They use the already-made powerful JavaScript file upload API to make file upload easy and stressless.
Let’s understand what a file uploader is, what it looks like, its features, how it works, and lastly how to integrate a powerful JS file uploader API into our JS web application.
What Really is a File Uploader?
A JS file uploader is a GUI component that allows the uploading of one or more files to a web server. Through these file uploaders, you can upload different types of files, including pictures, documents, audio files, and videos.
A common feature of several websites and apps is the ability to upload files. File inputs included in HTML forms were previously used to do this. This solution, however, has various drawbacks and necessitates a lot more coding. Javascript File Uploaders are useful in this situation.
With a number of complex capabilities that HTML file uploaders lack, JS File Uploaders make uploading files extremely simple because they allow:
- Drag and drop functionality
- File Validation
- Countless sources of uploads
- Fast image processing and transformation speed
And many more…
Because of ready-made file uploaders like Filestack uploader, developers no longer have to write long lines of code to implement file uploading functionalities in their web application projects, instead, they integrate these already-made file uploaders using as little as two lines of code.
Now we know what a file uploader is, let’s learn what are the features of an Ideal File Uploader for your next Web App project.
Features of an Ideal File Uploader
1. Friendly User Interface: Every user wants to explore a website that allows them to do more with less input of their energy. They want an all-in-one solution to their problems. The ideal file uploader must have a beautiful and customizable UI that makes uploading from any source painless and intuitive. Here are some features of a user-friendly file uploader.
- Preview feature: the user should have the option of previewing their files before uploading so they can be sure they picked the correct files.
- Multiple Uploads: a user wants to have the ability to select multiple files at a time instead of going back and forth to upload several files.
- Responsive interface: a file uploader should let the user use it consistently across all devices.
- Fast upload: this is to ensure that a user’s file is uploaded as quickly as possible regardless of the size.
2. Different sources of file upload: By far the most common and easiest form of file upload is users uploading files from their device storage. But with the provision of cloud storage, we might need to integrate a wide range of options for users to upload their files. With a solid third-party file upload solution, you can connect to content directly from the sources where your users have it without the hassle or effort often involved. Examples of file sources across different platforms are:
- Cloud storage: uploading from cloud storage like Google Drive, iCloud, Mega, Dropbox etc. as mentioned
- Remote URL: uploading by copying and pasting a link into the dialogue box.
- Social media accounts such as Facebook, Instagram etc.
Not every website needs this, but giving a user numerous options always makes a website more user-friendly.
3. Secured file uploads With the rise of cybercrime and stealing of digital information, it is worthy to take potential security risks into consideration. These include malware injection, hosting of illegal files, stealing of users’ data and so on. Hence, there’s a need to authenticate file upload forms. Here are some security practices that can help you secure files:
- Make a list of allowed file types: This depends on the kind of business you are in and what files you need to collect. This list is to determine what types of files you’ll accept or reject. I’m sure that accepting executable files is a bad idea, because these kinds of files might be capable of executing commands and running malicious code.
- Use of multiple anti-malware tools to scan uploaded files.
- To avoid the occurrence of a service outage, set a maximum and minimum file size.
- Setting up SSL encryption to secure the passage of data between the web server and the browser.
4. Image Editing Within the Browser
Users might need to edit images to align with the design of the website or app. Well-designed file uploaders should provide this functionality that aids these kinds of manipulations inside the browser: crop, resize, rotate, and so on.
How To Integrate JS File Uploads Into Web Forms?
Custom JS File Uploader
Set up the Node.js server
We will make use of the inbuilt HTTP package to build up the backend server.
Let’s start by creating a new folder for the project.
mkdir myfileuploader |
Then, we need to create an index.js file that would be the entry point of our backend server.
touch index.js |
After this, write the code to start the HTTP server.
const http = require(‘http’); // import http module const server = http.createServer(); // create server server.listen(5000, () => { |
In the above code, we have created an HTTP server, running on port 5000.
Building the Frontend
Next, we build up the client-side of our project. On our frontend, we will create a basic HTML file which will contain an input field and a submit button to upload our file when clicked. There would be a status text that would show the status of the file uploading process.
In vanilla JS, we use an event handler to perform an action on a button click.
<html lang=”en”> <head> <meta charset=”UTF-8″> <meta name=”viewport” content=”width=device-width, initial-scale=1.0″> <title>File Uploader</title> </head> <body> <h2>File Upload Service</h2> <input type=”file” id=”file”> <button id=”upload”>Upload</button> <small id=”status”></small> <script> |
Users can select the file and upload it by clicking on the upload button.
To render this HTML file on calling the root route, we would need to interact with our backend. The easiest approach is by using the server.on(‘request’) method to listen to all network requests in a Node backend server.
server.on(‘request’, (req, res) => {
if(req.url === ‘/’ && req.method === ‘GET’) { |
Accessing the file content on the client-side
After starting our backend server, we need to be able to read the file on the client-side. To do so, we are going to use the FileReader object, which allows web applications to asynchronously access the contents of files (or raw data buffers) saved in the user’s computer.
The syntax to access a file on the client-side using the FileReader object is:
const fileReader = new FileReader(); // initialize the object fileReader.readAsArrayBuffer(file); // read file as array buffer |
Under the files array for the input, we have access to certain input files. Although we are just currently creating it to support single file uploads, we can eventually expand it to support multiple file uploads as well.
const selectFile = file.files[0]; |
FileReader gives us access to a couple of methods which gives us access to the files from the backend to the frontend.
- FileReader.readAsBinaryString() — read the file in raw binary data
- FileReader.readAsArrayBuffer() — read file as array buffer
- FileReader.readAsText() — If we know the type of the file to be a text, this method is useful
- FileReader.readAsDataURL() — read the file and send the result as a data URL
For our project, we will use the readAsArrayBuffer method to read the file in small chunks and stream it to the backend server.
To monitor the client-side reading of the file, FileReader provides some event listeners such as onload, onprogress etc.
Here we would like to split our files into bytes and send them to the backend server, and we will be using the onload event handler, which will be initiated immediately after the reading of the file is completed.
We might have considered using the onprogress method to make the application a fully streamable file upload process. But the problem with the onprogress method is that it does not tell us about the newly read file chunk; instead, it tells us the data read up until now. So, we go with the onload method.
Once the file is completely read, we split it into small bytes and send it to the backend.
<!DOCTYPE html> <html lang=”en”> <head> <meta charset=”UTF-8″> <meta name=”viewport” content=”width=device-width, initial-scale=1.0″> <title>File Uploader</title> </head> <body> <h2>File Upload Service</h2> <input type=”file” id=”file”> <button id=”upload”>Upload</button> <small id=”status”></small> <script> const file = document.getElementById(‘file’); const upload = document.getElementById(‘upload’); const status = document.getElementById(status); upload.addEventListener(‘click’, () => { // set status to uploading status.innerHTML = ‘uploading…’; const fileReader = new FileReader(); fileReader.readAsArrayBuffer(file.files[0]); fileReader.onload = (event) => { console.log(‘Complete File read successfully!’) } }); </script> </body> </html> |
We are using a <small> HTML tag that displays uploading… as soon as we start uploading and changes to uploaded!!! Once the file is successfully uploaded to the backend.
Split and send the file in bytes to the backend
Sending the entire file at once is not a smart idea because file sizes can occasionally be huge. Some proxy servers such as Nginx might suspect it to be malicious because of the file’s huge size. Therefore, we will split this file into chunks of about 5000 bytes and send each one to the backend individually.
Once the file has been read, we may access its contents as an array buffer in the event.target.result field if we carefully examine the event argument.
This file’s array buffer will be divided into 5000-byte chunks by our program.
// file content const content = event.target.result; // fix chunk size const CHUNK_SIZE = 5000; // total chunks const totalChunks = event.target.result.byteLength / CHUNK_SIZE; // loop over each chunk // todo – send it to the backend |
These chunks must now be sent to the backend. Our old friend fetch is here to hit the backend server.
To prevent the file from becoming corrupted, we must transmit the chunks to the backend in the correct order.
Because we don’t want to overwhelm the backend server with queries, the second thing to do is to use async await while uploading.
fileReader.onload = async (event) => {
const content = event.target.result; // generate a file name for (let chunk = 0; chunk < totalChunks + 1; chunk++) { await fetch(‘/upload?fileName=’ + fileName, { |
In the code above, we added the file name as a query parameter, because we need to append the content to a file, and it has to be a unique identifier, and in our case that would be the file name.
The user might want to upload the file with the same file name so we have to ensure that the backend does its work as expected, therefore, need a unique identifier. And for that, we use this beautiful one-liner:
Math.random().toString(36).slice(-6) |
We won’t send any custom header because most of the proxies such as Nginx or HAProxy might block it.
Collect the chunks and store them on the backend
Since we are done setting up the client-side, the next step is to await the arrival of the file chunks and store them on the server.
To get the file name from the query params of the request, we use the below piece of code.
const query = new URLSearchParams(req.url); const fileName = query.get(‘/upload?fileName’); |
So, our code looks like this:
server.on(‘request’, (req, res) => {
if(req.url === ‘/’ && req.method == ‘GET’) { if(req.url=== ‘/upload’ && req.method == ‘POST’) { req.on(‘data’, chunk => { return res.end(‘Yay! File is uploaded.’) |
Uploading more than one file
With vanilla JS, we have so far created a stunning application for single file uploads. Our next objective is to add support for multiple file uploads to our current implementation.
We will get started now.
When we look at it carefully, we can see that the backend is clever enough to handle multiple file uploads as well because it only needs to do one thing: take a chunk and add it to the appropriate file name that was received in the request. No matter how many files are being uploaded from the front end, it is absolutely unaffected.
So, let’s take advantage of it and improve our application for it.
Modifying the file input is the first step in allowing multiple file selections on the user interface. It now accepts input as a single file by default. We use the multiple input option to accept multiple files:
<input type=”file” id=”files” multiple> |
With this one line of code, we are ready to receive multiple files from our user input and also we changed the id of our input tag from file to file.
We now know that all input files can now be accessible from the files.files array. So, the next step is very simple: we will loop over the array of selected files, divide it into small bytes one by one, and send it to the backend server and store it there:
for(let fileIndex=0;fileIndex<files.files.length;fileIndex++) { const file = files.files[fileIndex]; // divide the file into chunks and upload it to the backend |
With for loop it makes it very simple to iterate over each file and upload it to the backend.
To monitor our file upload status, we keep a variable that gets updated when a file uploads.
So, our script looks like this:
const files = document.getElementById(‘files’); const upload = document.getElementById(‘upload’); const status = document.getElementById(‘status’); upload.addEventListener(‘click’, () => { // set loading status for (let fileIndex = 0; fileIndex < files.files.length; fileIndex++) { const fileReader = new FileReader(); fileReader.readAsArrayBuffer(file); fileReader.onload = async (event) => { const fileName = Math.random().toString(36).slice(-6) + file.name; for (let chunk = 0; chunk < totalChunks + 1; chunk++) { await fetch(‘/upload?fileName=’ + fileName, { status.innerHTML = `file ${fileUploaded} of ${files.files.length} uploaded!!!`; }) |
Looking at our code, you should notice that we have achieved multiple file uploads in parallel as well. Take a look at the network tab on your developer console, you will see that file chunks are being uploaded in parallel, but yes, files are themselves being uploaded serially.
We are not waiting for the previous file to upload completely, all the files are being uploaded individually. As our backend is stateless, this functionality is perfectly working.
In conclusion, we now know how to build a file upload service with pure JavaScript. Evidently, this is the most efficient approach, but it’s enough to give you a fair understanding of a lot of core concepts about file uploading.
Third-party File Uploader
Filestack comes with comprehensive yet very user-friendly documentation. This section will give you a basic understanding of how to get started with Filestack File Uploader.
- Sign up for a Filestack account.
Filestack comes with a free plan to allow users to try their file uploader before buying. This free plan includes 1GB of storage, 500 uploads, 1 GB bandwidth, and 1000 transformations. The premium plans cost between $59 to $359 per month and include much more features.
- Retrieve the API keys
After creating your Filestack account, navigate to the developer portal and obtain your secret and API key by creating an application there. You will get a unique secret and API key for each application you create. These keys will be helpful to authorize and authenticate any actions performed on your resources. Moreover, you can apply different settings to each application you create. It enables you to apply specific settings to different file uploaders.
- Setup the File Picker
You can set up the frontend File Picker by including the JavaScript SDK UMD module in the head section of your code, as shown below.
<script src=”//static.filestackapi.com/filestack-js/3.x.x/filestack.min.js”> </script> |
Then, configure the client using your API key:
const client = filestack.init( YOUR_API_KEY ); client.picker().open(); |
Following is an example HTML file that includes a minimal and the default File Picker.
<!DOCTYPE html> <html> <head> <script src = “//static.filestackapi.com/filestack-js/3.x.x/filestack.min.js”> </script> </head> <body> <script> const client = filestack.init (API_KEY); client.picker().open(); </script> |
That’s it, and you have successfully integrated the FileStack file uploader into your web page. The above example will output a basic file uploader with the default settings. However, you can apply many options and configurations to the above setup.
Conclusion
As you can see, creating a file uploader from scratch can be really strenuous and time-consuming as compared to using a third-party library.
You can use any of these methods, depending on your need to integrate a file uploader in your next web application.