![Kayce Basques](https://web.dev/images/authors/kaycebasques.jpg) Kayce Basques [X](https://twitter.com/kaycebasques) [GitHub](https://github.com/kaycebasques) ![Pete LePage](https://web.dev/images/authors/petelepage.jpg) Pete LePage [X](https://twitter.com/petele) [GitHub](https://github.com/petele) [Mastodon](https://techhub.social/@petele) [Homepage](https://petelepage.com/) ![Thomas Steiner](https://web.dev/images/authors/thomassteiner.jpg) Thomas Steiner [GitHub](https://github.com/tomayac) [LinkedIn](https://www.linkedin.com/in/thomassteinerlinkedin) [Mastodon](https://toot.cafe/@tomayac) [Bluesky](https://bsky.app/profile/tomayac.com) [Homepage](https://blog.tomayac.com/)

<br />

Selecting and interacting with files on the user's local device is one of the
most commonly used features of the web. It allows users to select files and
upload them to a server, for example, when sharing photos or submitting tax
documents. It also allows sites to read and manipulate them without ever having
to transfer the data across the network. This page walks through how to use
JavaScript to interact with files.

## The modern File System Access API

The File System Access API provides a way to read from and write to files and
directories on the user's local system. It's available in most Chromium-based
browsers such as Chrome and Edge. To learn more about it, refer to
[The File System Access API](https://developer.chrome.com/docs/capabilities/web-apis/file-system-access).

Because the File System Access API isn't compatible with all browsers, we
recommend using [browser-fs-access](https://github.com/GoogleChromeLabs/browser-fs-access),
a helper library that uses the new API wherever it's available and falls
back to legacy approaches when it isn't.

## Work with files, the classic way

This guide shows you how to interact with files using legacy JavaScript methods.

## Select files

There are two primary ways of selecting files: using
[the HTML input element](https://web.dev/articles/read-files.md.txt#select-input), and using a
[drag-and-drop zone](https://web.dev/articles/read-files.md.txt#select-dnd).

### HTML input element

The easiest way for users to select files is using the
[`<input type="file">`](https://developer.mozilla.org/docs/Web/HTML/Element/input/file)
element, which is supported in every major browser. When clicked, it lets a user
select a file, or multiple files if the
[`multiple`](https://developer.mozilla.org/docs/Web/HTML/Element/input/file#Additional_attributes)
attribute is included, using their operating system's built-in file selection
UI. When the user finishes selecting a file or files, the element's `change`
event fires. You can access the list of files from `event.target.files`, which
is a [`FileList`](https://developer.mozilla.org/docs/Web/API/FileList) object.
Each item in the `FileList` is a [`File`](https://developer.mozilla.org/docs/Web/API/File) object.

```html
<!-- The `multiple` attribute lets users select multiple files. -->
<input type="file" id="file-selector" multiple>
<script>
  const fileSelector = document.getElementById('file-selector');
  fileSelector.addEventListener('change', (event) => {
    const fileList = event.target.files;
    console.log(fileList);
  });
</script>
```

> [!NOTE]
> **Note:** Check whether the [`window.showOpenFilePicker()`](https://developer.chrome.com/articles/file-system-access/#ask-the-user-to-pick-a-file-to-read) method is a viable alternative for your use case. It provides a file handle so you can write back to a file, in addition to reading. This method can be [polyfilled](https://github.com/GoogleChromeLabs/browser-fs-access#opening-files).

The following example lets a user select multiple files using their operating
system's built-in file selection UI and then logs each selected file to the
console.
<iframe allow="camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi" loading="lazy" src="https://googlechrome.github.io/samples/input-type-file/" style="height: 100%; width: 100%; border: 0;"></iframe>

#### Limit the types of files users can select

In some cases, you might want to limit the types of files users can select. For
example, an image editing app should only accept images, not text files. To set
file type restrictions, add an
[`accept`](https://developer.mozilla.org/docs/Web/HTML/Element/input/file#Additional_attributes)
attribute to the input element to specify which file types are accepted:

```html
<input type="file" id="file-selector" accept=".jpg, .jpeg, .png">
```

### Custom drag-and-drop

In some browsers, the `<input type="file">` element is also a drop target,
allowing users to drag-and-drop files into your app. However, this drop target
is small and can be hard to use. Instead, after you provide core features using
an `<input type="file">` element, you can provide a large, custom drag-and-drop
surface.

> [!NOTE]
> **Note:** Check whether the [`DataTransferItem.getAsFileSystemHandle()`](https://developer.chrome.com/articles/file-system-access/#drag-and-drop-integration) method is a viable alternative for your use case. It provides a file handle so you can write back to a file, in addition to reading.

#### Choose your drop zone

Your drop surface depends on the design of your application. You might only want
part of the window to be a drop surface, but you can use the entire window.
![A screenshot of Squoosh, an image compression web app.](https://web.dev/static/articles/read-files/image/a-screenshot-squoosh-i-9973c866302e4.png) Squoosh makes the entire window a drop zone.

The image compression app Squoosh lets user drag an image anywhere into the
window, and click **select an image** to invoke the `<input type="file">`
element. Whatever you choose as your drop zone, make sure it's clear to the user
that they can drag files onto that surface.

#### Define the drop zone

To enable an element as a drag-and-drop zone, create listeners for
two events: [`dragover`](https://developer.mozilla.org/docs/Web/API/Document/dragover_event)
and [`drop`](https://developer.mozilla.org/docs/Web/API/Document/drop_event).
The `dragover` event updates the browser UI to visually indicate that the
drag-and-drop action is creating a copy of the file. The `drop` event fires
after the user drops the files onto the surface. As with the input element, you
can access the list of files from `event.dataTransfer.files`, which is a
[`FileList`](https://developer.mozilla.org/docs/Web/API/FileList) object. Each
item in the `FileList` is a [`File`](https://developer.mozilla.org/docs/Web/API/File) object.

```javascript
const dropArea = document.getElementById('drop-area');

dropArea.addEventListener('dragover', (event) => {
  event.stopPropagation();
  event.preventDefault();
  // Style the drag-and-drop as a "copy file" operation.
  event.dataTransfer.dropEffect = 'copy';
});

dropArea.addEventListener('drop', (event) => {
  event.stopPropagation();
  event.preventDefault();
  const fileList = event.dataTransfer.files;
  console.log(fileList);
});
```

[`event.stopPropagation()`](https://developer.mozilla.org/docs/Web/API/Event/stopPropagation)
and [`event.preventDefault()`](https://developer.mozilla.org/docs/Web/API/Event/preventDefault)
stop the browser's default behavior and let your code run instead. Without them,
the browser would otherwise navigate away from your page and open the files
the user dropped into the browser window.

For a live demonstration, refer to
[Custom drag-and-drop](https://googlechrome.github.io/samples/custom-drag-and-drop/).

### What about directories?

Unfortunately, there isn't a good way to access a directory using JavaScript.

The [`webkitdirectory`](https://developer.mozilla.org/docs/Web/API/HTMLInputElement/webkitdirectory)
attribute on the `<input type="file">` element lets the user choose a directory
or directories. It's [supported in most major browsers](https://caniuse.com/#search=webkitdirectory)
except for Firefox for Android and Safari on iOS.

> [!NOTE]
> **Note:** Check whether the [`window.showDirectoryPicker()`](https://developer.chrome.com/articles/file-system-access/#opening-a-directory-and-enumerating-its-contents) method is a viable alternative for your use case. It provides a directory handle so you can write back to a directory, in addition to reading. This method can be [polyfilled](https://github.com/GoogleChromeLabs/browser-fs-access#opening-directories).

If drag-and-drop is enabled, a user might try to drag a directory into the
drop zone. When the drop event fires, it includes a `File` object for the
directory, but doesn't provide access to any of the files in the directory.

## Read file metadata

The [`File`](https://developer.mozilla.org/docs/Web/API/File) object contains metadata about the file. Most browsers
provide the filename, the size of the file, and the MIME type, though depending
on the platform, different browsers might provide different or additional
information.

```javascript
function getMetadataForFileList(fileList) {
  for (const file of fileList) {
    // Not supported in Safari for iOS.
    const name = file.name ? file.name : 'NOT SUPPORTED';
    // Not supported in Firefox for Android or Opera for Android.
    const type = file.type ? file.type : 'NOT SUPPORTED';
    // Unknown cross-browser support.
    const size = file.size ? file.size : 'NOT SUPPORTED';
    console.log({file, name, type, size});
  }
}
```

You can see this in action in the [`input-type-file`](https://googlechrome.github.io/samples/input-type-file/)
demo.

## Read a file's content

Use [`FileReader`](https://developer.mozilla.org/docs/Web/API/FileReader) to
read the content of a `File` object into memory. You can tell `FileReader` to
read a file as an [array buffer](https://developer.mozilla.org/docs/Web/API/FileReader/readAsArrayBuffer),
a [data URL](https://developer.mozilla.org/docs/Web/API/FileReader/readAsDataURL),
or [text](https://developer.mozilla.org/docs/Web/API/FileReader/readAsText):

```javascript
function readImage(file) {
  // Check if the file is an image.
  if (file.type && !file.type.startsWith('image/')) {
    console.log('File is not an image.', file.type, file);
    return;
  }

  const reader = new FileReader();
  reader.addEventListener('load', (event) => {
    img.src = event.target.result;
  });
  reader.readAsDataURL(file);
}
```

This example reads a `File` provided by the user, then converts it to a data
URL, and uses that data URL to display the image in an `img` element.
To learn how to verify that the user has selected an image file, refer to the
[`read-image-file`](https://googlechrome.github.io/samples/read-an-image-file/) demo.
<iframe allow="camera; clipboard-read; clipboard-write; encrypted-media; geolocation; microphone; midi" loading="lazy" src="https://googlechrome.github.io/samples/read-an-image-file" style="height: 100%; width: 100%; border: 0;"></iframe>

### Monitor the progress of a file read

When reading large files, it can be helpful to provide some UX to tell the user
how far the read has progressed. For that, use the
[`progress`](https://developer.mozilla.org/docs/Web/API/FileReader/progress_event)
event provided by `FileReader`. The `progress` event has two properties:
`loaded` (the amount read) and `total` (the amount to read).

    function readFile(file) {
      const reader = new FileReader();
      reader.addEventListener('load', (event) => {
        const result = event.target.result;
        // Do something with result
      });

      reader.addEventListener('progress', (event) => {
        if (event.loaded && event.total) {
          const percent = (event.loaded / event.total) * 100;
          console.log(`Progress: ${Math.round(percent)}`);
        }
      });
      reader.readAsDataURL(file);
    }