List keys under a specified path, as well as `lastModified` timestamp.

<!-- TODO Rewrite the details about prefixes here appear to be things that need to be added to ioS/Android/Flutter? Or is that too much info? -->

## Public level list

```javascript
Storage.list('photos/') // for listing ALL files without prefix, pass '' instead
  .then(({ results }) => console.log(results))
  .catch((err) => console.log(err));
```

Note the trailing slash `/` - if you had requested `Storage.list('photos')` it would also match against files like `photos123.jpg` alongside `photos/123.jpg`.

The format of the response will look similar to the below example

```js
{
  results: [
    {
      eTag: ""30074401292215403a42b0739f3b5262"",
      key: "123.png",
      lastModified: "Thu Oct 08 2020 23:59:31 GMT+0800 (Singapore Standard Time)",
      size: 138256
    },
    // ...
  ],
  hasNextToken: false
}
```

Manually created folders will show up as files with a `size` of 0, but you can also match keys against a regex like `file.key.match(/\.[0-9a-z]+$/i)` to distinguish files vs folders. Since "folders" are a virtual concept in S3, any file may declare any depth of folder just by having a `/` in its name. If you need to list all the folders, you'll have to parse them accordingly to get an authoritative list of files and folders:

```js
function processStorageList(response) {
  let files = [];
  let folders = new Set();
  response.results.forEach((res) => {
    if (res.size) {
      files.push(res);
      // sometimes files declare a folder with a / within then
      let possibleFolder = res.key
        .split('/')
        .slice(0, -1)
        .join('/');
      if (possibleFolder) folders.add(possibleFolder);
    } else {
      folders.add(res.key);
    }
  });
  return { files, folders };
}
```

If you need the files and folders in terms of a nested object instead (for example, to build an explorer UI), you could parse it recursively:

```js
function processStorageList(response) {
  const filesystem = {};
  // https://stackoverflow.com/questions/44759750/how-can-i-create-a-nested-object-representation-of-a-folder-structure
  const add = (source, target, item) => {
    const elements = source.split('/');
    const element = elements.shift();
    if (!element) return; // blank
    target[element] = target[element] || { __data: item }; // element;
    if (elements.length) {
      target[element] =
        typeof target[element] === 'object' ? target[element] : {};
      add(elements.join('/'), target[element], item);
    }
  };
  response.results.forEach((item) => add(item.key, filesystem, item));
  return filesystem;
}
```

This places each item's data inside a special `__data` key.

## Protected level list

To list current user's objects:

```javascript
Storage.list('photos/', { level: 'protected' })
  .then(({ results }) => console.log(results))
  .catch((err) => console.log(err));
```

To get other users' objects:

```javascript
Storage.list('photos/', {
  level: 'protected',
  identityId: 'xxxxxxx' // the identityId of that user
})
  .then(({ results }) => console.log(results))
  .catch((err) => console.log(err));
```

## Private level list

```javascript
Storage.list('photos/', { level: 'private' })
  .then(({ results }) => console.log(results))
  .catch((err) => console.log(err));
```

## Access all files

To get a list of all files in your S3 bucket under a specific prefix, you can set pageSize to ALL. The default value of pageSize is 1000.

```javascript
Storage.list('photos/', { pageSize: 'ALL' })
  .then(({ results }) => console.log(result))
  .catch((err) => console.log(err));
```

## Paginated file access

If the pageSize is set lower than the total file size, a single `Storage.list` call only returns a subset of all the files. To list all the files with multiple calls, users can use the `hasNextToken` and `nextToken` keys:

```javascript
const PAGE_SIZE = 20;
let nextToken = undefined;
let hasNextPage = true;
//...
const loadNextPage = async () => {
  if (hasNextPage) {
    let response = await Storage.list('', {
      pageSize: PAGE_SIZE,
      nextToken: nextToken
    });
    if (response.hasNextToken) {
      nextToken = response.nextToken;
    } else {
      nextToken = undefined;
      hasNextPage = false;
    }
    // render list items from response.results
  }
};
```

<Callout>Note: The range of pageSize can be from 0 - 1000 or 'ALL'.</Callout>
