10 useful HTML file upload tips for web developers
Tags: html, javascript, webdev, codenewbie
Published: 2020-10-20 05:31:31

# Introduction
The ability to upload files is a key requirement for many web and mobile applications. From uploading your photo on social media to post your resume on a job portal website, `file upload` is everywhere.

As a web developer, we must know that HTML provides the support of native file upload with a bit of help from JavaScript. With `HTML5` the File API is added to the DOM. Using that, we can read the `FileList` and the `File` Object within it. This solves multiple use-cases with files, i.e, load them locally or send over the network to a server for processing, etc.

In this article, we will discuss 10 such usages of HTML file upload support. Hope you find it useful.

# TL;DR
At any point in time, if you want to play with these `file upload` features, you can find it from here,

- HTML File Upload Demo: [https://html-file-upload.netlify.app/](https://html-file-upload.netlify.app/)

The source code of the demo is in my Github repo. ✋ Feel free to follow as I keep the code updated with examples. Please give a ⭐ if you find it useful.

-  Source Code Repo: [https://github.com/atapas/html-file-upload](https://github.com/atapas/html-file-upload)

# 1. Simple file upload
We can specify the input type as `file` to use the file uploader functionality in a web application. 

```html
<input type="file" id="file-uploader">
```
An input file type enables users with a button to upload one or more files. By default, it allows uploading a single file using the operating system's native file browser. 

On successful upload, the `File API` makes it possible to read the `File` object using simple JavaScript code. To read the `File` object, we need to listen to the `change` event of the file uploader.

First, get the file uploader instance by id,

```js
const fileUploader = document.getElementById('file-uploader');
```

Then add a `change` event listener to read the file object when the upload completes. We get the uploaded file information from the `event.target.files` property.

```js
fileUploader.addEventListener('change', (event) => {
  const files = event.target.files;
  console.log('files', files);
});
```
Observe the output in the browser console. Note the `FileList` array with the `File` object having all the metadata information about the uploaded file.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1602998726413/TQRtgSbfX.png?border=1,CCCCCC&auto=compress)

Here is the CodePen for you with the same example to explore further

{% codepen https://codepen.io/atapas/pen/rNLOyRm %}

# 2. Multiple file uploads
We can upload multiple files at a time. To do that, we just need to add an attribute called, `multiple` to the input file tag.

```html
<input type="file" id="file-uploader" multiple />
```
Now, the file browser will allow you to upload one or more files to upload. Just like the previous example, you can add a `change` event handler to capture the information about the files uploaded. Have you noticed, the `FileList` is an array? Right, for `multiple` file uploads the array will have information as,

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1602999227916/9Gf56XCN2.png?border=1,CCCCCC&auto=compress)

Here is the CodePen link to explore multiple file uploads.

{% codepen https://codepen.io/atapas/pen/MWeamYp %}

# 3. Know about file metadata
Whenever we upload a file, the `File` object has the metadata information like file name, size, last update time, type, etc. This information can be useful for further validations, decision-making.

```js
// Get the file uploader by id
const fileUploader = document.getElementById('file-uploader');

// Listen to the change event and read metadata
fileUploader.addEventListener('change', (event) => {
  // Get the FileList array
  const files = event.target.files;
  
  // Loop through the files and get metadata
  for (const file of files) {
    const name = file.name;
    const type = file.type ? file.type: 'NA';
    const size = file.size;
    const lastModified = file.lastModified;
    console.log({ file, name, type, size, lastModified });
  }
});
```
Here is the output for single file upload,

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603000111928/7Q5sILHtT.png?border=1,CCCCCC&auto=compress)

Use this CodePen to explore further,

{% codepen https://codepen.io/atapas/pen/gOMaRJv %}

# 4. Know about file `accept` property
We can use the `accept` attribute to limit the type of files to upload. You may want to show only the allowed types of images to browse from when a user is uploading a profile picture.

```html
<input type="file" id="file-uploader" accept=".jpg, .png" multiple>
```
In the code above, the file browser will allow only the files with the extension `jpg` and `png`.

Note, in this case, the file browser automatically sets the file selection type as custom instead of all. However, you can always change it back to all files, if required.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603000626987/tpSORAgzU.png?border=1,CCCCCC&auto=compress)

Use this CodePen to explore the `accept` attribute,

{% codepen https://codepen.io/atapas/pen/OJXymRP %}

# 5. Manage file content
You may want to show the file content after a successful upload of it. For profile pictures, it will be confusing if we do not show the uploaded picture to the user immediately after upload.

We can use the `FileReader` object to convert the file to a binary string. Then add a `load` event listener to get the binary string on successful file upload.

```js
// Get the instance of the FileReader
const reader = new FileReader();

fileUploader.addEventListener('change', (event) => {
  const files = event.target.files;
  const file = files[0];

  // Get the file object after upload and read the
  // data as URL binary string
  reader.readAsDataURL(file);

  // Once loaded, do something with the string
  reader.addEventListener('load', (event) => {
    // Here we are creating an image tag and adding
    // an image to it.
    const img = document.createElement('img');
    imageGrid.appendChild(img);
    img.src = event.target.result;
    img.alt = file.name;
  });
});
```

Try selecting an image file in the CodePen below and see it renders.

{% codepen https://codepen.io/atapas/pen/zYBvdjZ %}

# 6. Validate file size
As we have seen, we can read the size metadata of a file, we can actually use it for a file size validation. You may allow users to upload an image file up to 1MB. Let us see how to achieve that.

```js
// Listener for file upload change event
fileUploader.addEventListener('change', (event) => {
  // Read the file size
  const file = event.target.files[0];
  const size = file.size;
  
  let msg = '';
  
 // Check if the file size is bigger than 1MB and prepare a message.
  if (size > 1024 * 1024) {
      msg = `<span style="color:red;">The allowed file size is 1MB. The file you are trying to upload is of ${returnFileSize(size)}</span>`;
  } else {
      msg = `<span style="color:green;"> A ${returnFileSize(size)} file has been uploaded successfully. </span>`;
  }
  
  // Show the message to the user
  feedback.innerHTML = msg;
});
```
Try uploading a file of different sizes to see how the validation works,

{% codepen https://codepen.io/atapas/pen/pobjMKv %}

# 7. Show file upload progress
The better usability is to let your users know about a file upload progress. We are now aware of the `FileReader` and the event to read and load the file.

```js
const reader = new FileReader();
```

The `FileReader` has another event called, `progress` to know how much has been loaded. We can use HTML5's `progress` tag to create a progress bar with this information.

```js
reader.addEventListener('progress', (event) => {
  if (event.loaded && event.total) {
    // Calculate the percentage completed
    const percent = (event.loaded / event.total) * 100;
    // Set the value to the progress component
    progress.value = percent;
  }
});
```
How about you try uploading a bigger file and see the progress bar working in the CodePen below? Give it a try.

{% codepen https://codepen.io/atapas/pen/eYzpwYj %}

# 8. How about directory upload?
Can we upload an entire directory? Well, it is possible but with some limitations. There is a non-standard attribute(at least, while writing this article) called, `webkitdirectory` that allows us to upload an entire directory.

> Though originally implemented only for WebKit-based browsers, webkitdirectory is also usable in Microsoft Edge as well as Firefox 50 and later. However, even though it has relatively broad support, it is still not standard and should not be used unless you have no alternative.

You can specify this attribute as,

```html
<input type="file" id="file-uploader" webkitdirectory />
```
This will allow you to select a folder(aka, directory),

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603002578975/b257wmsCz.png?border=1,CCCCCC&auto=compress)

User has to provide a confirmation to upload a directory,

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603002718517/K9d06RU7D.png?border=1,CCCCCC&auto=compress)

Once the user clicks the Upload button, the uploading takes place. One important point to note here. The `FileList` array will have information about all the files in the uploaded directory as a flat structure. But the key is, for each of the `File` objects, the `webkitRelativePath` attribute will have the directory path.

For example, let us consider a `main` directory and other folders and files under it,

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603003268191/kubVgbpIX.png?border=1,CCCCCC&auto=compress)

Now the `File` objects will have the `webkitRelativePath` populated as,

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603003433275/lkOzOZZSJ.png?border=1,CCCCCC&auto=compress)

You can use it to render the folder and files in any UI structure of your choice. Use this CodePen to explore further.

{% codepen https://codepen.io/atapas/pen/dyXYRKp %}

# 9. Let's drag, drop and upload
Not supporting a drag-and-drop for file upload is kinda old fashion, isn't it? Let us see how to achieve that with a few simple steps.

First, create a drop zone and optionally a section to show the uploaded file content. We will use an image as a file to drag and drop here.

```html
<div id="container">
  <h1>Drag & Drop an Image</h1>
  <div id="drop-zone">
    DROP HERE
  </div>

  <div id="content">
    Your image to appear here..
  </div>

</div>
```
Get the dropzone and the content areas by their respective ids.

```js
 const dropZone = document.getElementById('drop-zone');
 const content = document.getElementById('content');
```
Add a `dragover` event handler to show the effect of something going to be copied,

```js
dropZone.addEventListener('dragover', event => {
  event.stopPropagation();
  event.preventDefault();
  event.dataTransfer.dropEffect = 'copy';
});
```

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1603004031609/ClG54o6Dv.png?border=1,CCCCCC&auto=compress)

Next, define what we want to do when the image is dropped. We will need a `drop` event listener to handle that.

```js
dropZone.addEventListener('drop', event => {
  // Get the files
  const files = event.dataTransfer.files;

// Now we can do everything possible to show the
// file content in an HTML element like, DIV
});
```

Try to drag and drop an image file in the CodePen example below and see how it works. Do not forget to see the code to render the dropped image as well.

{% codepen https://codepen.io/atapas/pen/ExyVoXN %}

# 10. Handle files with objectURLs
There is a special method called, `URL.createObjectURL()` to create an unique URL from the file. You can also release it by using `URL.revokeObjectURL()` method.

> The DOM `URL.createObjectURL()` and `URL.revokeObjectURL()` methods let you create simple URL strings that can be used to reference any data that can be referred to using a DOM File object, including local files on the user's computer.

A simple usage of the object URL is,

```js
img.src = URL.createObjectURL(file);
```

Use this CodePen to explore the object URL further. Hint: Compare this approach with the approach mentioned in #5 previously.

{% codepen https://codepen.io/atapas/pen/BazzaoN %}

# Conclusion
I truly believe this,

{% twitter 1316754184016850944 %}

Many times a native HTML feature may be enough for us to deal with the use-cases in hands. I found, `file upload` is one such that provides many cool options by default.

Let me know if this article was useful to you by commenting below. You may also like,

- [10 useful HTML5 features, you may not be using](https://blog.greenroots.info/10-useful-html5-features-you-may-not-be-using-ckdua7ql300l1m3s1ez7teshc)
- [I made a photo gallery with CSS animation. Here’s what I learned.](https://blog.greenroots.info/i-made-a-photo-gallery-with-css-animation-heres-what-i-learned-ckfzbk6v903ea2xs14l1942f7)
- [10 lesser-known Web APIs you may want to use](https://blog.greenroots.info/10-lesser-known-web-apis-you-may-want-to-use-ckejv75cr012y70s158n85yhn)

The GitHub repo,

{% github atapas/html-file-upload %}

<hr />
If it was useful to you, please Like/Share so that, it reaches others as well.

You can @ me on Twitter ([@tapasadhikary](https://twitter.com/home)) with comments, or feel free to follow.