---
title: FileSystem
description: A library that provides access to the local file system on the device.
sourceCodeUrl: https://github.com/expo/expo/tree/sdk-53/packages/expo-file-system
packageName: expo-file-system
iconUrl: /static/images/packages/expo-file-system.png
platforms: ["android", "ios", "tvos"]
---
`expo-file-system` provides access to a file system stored locally on the device. It is also capable of uploading and downloading files from network URLs.
Within Expo Go, each project has a separate file system scope and has no access to the file system
of other projects.
## Installation
## Usage
### Downloading files
```js Component.js
const callback = downloadProgress => {
const progress = downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite;
this.setState({
downloadProgress: progress,
});
};
const downloadResumable = FileSystem.createDownloadResumable(
'http://techslides.com/demos/sample-videos/small.mp4',
FileSystem.documentDirectory + 'small.mp4',
{},
callback
);
try {
const { uri } = await downloadResumable.downloadAsync();
console.log('Finished downloading to ', uri);
} catch (e) {
console.error(e);
}
try {
await downloadResumable.pauseAsync();
console.log('Paused download operation, saving for future retrieval');
AsyncStorage.setItem('pausedDownload', JSON.stringify(downloadResumable.savable()));
} catch (e) {
console.error(e);
}
try {
const { uri } = await downloadResumable.resumeAsync();
console.log('Finished downloading to ', uri);
} catch (e) {
console.error(e);
}
//To resume a download across app restarts, assuming the DownloadResumable.savable() object was stored:
const downloadSnapshotJson = await AsyncStorage.getItem('pausedDownload');
const downloadSnapshot = JSON.parse(downloadSnapshotJson);
const downloadResumable = new FileSystem.DownloadResumable(
downloadSnapshot.url,
downloadSnapshot.fileUri,
downloadSnapshot.options,
callback,
downloadSnapshot.resumeData
);
try {
const { uri } = await downloadResumable.resumeAsync();
console.log('Finished downloading to ', uri);
} catch (e) {
console.error(e);
}
```
### Managing Giphy's
```js
const gifDir = FileSystem.cacheDirectory + 'giphy/';
const gifFileUri = (gifId: string) => gifDir + `gif_${gifId}_200.gif`;
const gifUrl = (gifId: string) => `https://media1.giphy.com/media/${gifId}/200.gif`;
// Checks if gif directory exists. If not, creates it
async function ensureDirExists() {
const dirInfo = await FileSystem.getInfoAsync(gifDir);
if (!dirInfo.exists) {
console.log("Gif directory doesn't exist, creating…");
await FileSystem.makeDirectoryAsync(gifDir, { intermediates: true });
}
}
// Downloads all gifs specified as array of IDs
try {
await ensureDirExists();
console.log('Downloading', gifIds.length, 'gif files…');
await Promise.all(gifIds.map(id => FileSystem.downloadAsync(gifUrl(id), gifFileUri(id))));
} catch (e) {
console.error("Couldn't download gif files:", e);
}
}
// Returns URI to our local gif file
// If our gif doesn't exist locally, it downloads it
await ensureDirExists();
const fileUri = gifFileUri(gifId);
const fileInfo = await FileSystem.getInfoAsync(fileUri);
if (!fileInfo.exists) {
console.log("Gif isn't cached locally. Downloading…");
await FileSystem.downloadAsync(gifUrl(gifId), fileUri);
}
return fileUri;
}
// Exports shareable URI - it can be shared outside your app
return FileSystem.getContentUriAsync(await getSingleGif(gifId));
}
// Deletes whole giphy directory with all its content
console.log('Deleting all GIF files…');
await FileSystem.deleteAsync(gifDir);
}
```
### Server: handling multipart requests
The simple server in Node.js, which can save uploaded images to disk:
```js index.js
const express = require('express');
const app = express();
const fs = require('fs');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });
// This method will save the binary content of the request as a file.
app.patch('/binary-upload', (req, res) => {
req.pipe(fs.createWriteStream('./uploads/image' + Date.now() + '.png'));
res.end('OK');
});
// This method will save a "photo" field from the request as a file.
app.patch('/multipart-upload', upload.single('photo'), (req, res) => {
// You can access other HTTP parameters. They are located in the body object.
console.log(req.body);
res.end('OK');
});
app.listen(3000, () => {
console.log('Working on port 3000');
});
```
## API
```js
```
### Directories
The API takes `file://` URIs pointing to local files on the device to identify files. Each app only has read and write access to locations under the following directories:
- [`FileSystem.documentDirectory`](#filesystemdocumentdirectory)
- [`FileSystem.cacheDirectory`](#filesystemcachedirectory)
So, for example, the URI to a file named `'myFile'` under `'myDirectory'` in the app's user documents directory would be `FileSystem.documentDirectory + 'myDirectory/myFile'`.
Expo APIs that create files generally operate within these directories. This includes `Audio` recordings, `Camera` photos, `ImagePicker` results, `SQLite` databases and `takeSnapShotAsync()` results. This allows their use with the `FileSystem` API.
Some `FileSystem` functions are able to read from (but not write to) other locations.
### SAF URI
A SAF URI is a URI that is compatible with the Storage Access Framework. It should look like this `content://com.android.externalstorage.*`.
The easiest way to obtain such URI is by [`requestDirectoryPermissionsAsync`](#requestdirectorypermissionsasyncinitialfileurl) method.
## Supported URI schemes
In this table, you can see what type of URI can be handled by each method. For example, if you have an URI, which begins with `content://`, you cannot use `FileSystem.readAsStringAsync()`, but you can use `FileSystem.copyAsync()` which supports this scheme.
| Method name | Android | iOS |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
| `getInfoAsync` | `file:///`,<br/>`content://`,<br/>`asset://`,<br/>no scheme | `file://`,<br/>`ph://`,<br/>`assets-library://` |
| `readAsStringAsync` | `file:///`,<br/>`asset://`,<br/>[SAF URI](#saf-uri) | `file://` |
| `writeAsStringAsync` | `file:///`,<br/>[SAF URI](#saf-uri) | `file://` |
| `deleteAsync` | `file:///`,<br/>[SAF URI](#saf-uri) | `file://` |
| `moveAsync` | Source:<br/>`file:///`,<br/>[SAF URI](#saf-uri)<br/><br/>Destination:<br/>`file://` | Source:<br/>`file://`<br/><br/>Destination:<br/>`file://` |
| `copyAsync` | Source:<br/>`file:///`,<br/>`content://`,<br/>`asset://`,<br/>[SAF URI](#saf-uri),<br/>no scheme<br/><br/>Destination:<br/>`file://` | Source:<br/>`file://`,<br/>`ph://`,<br/>`assets-library://`<br/><br/>Destination:<br/>`file://` |
| `makeDirectoryAsync` | `file:///` | `file://` |
| `readDirectoryAsync` | `file:///` | `file://` |
| `downloadAsync` | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file:///` | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file://` |
| `uploadAsync` | Source:<br/>`file:///`<br/><br/>Destination:<br/>`http://`<br/>`https://` | Source:<br/>`file://`<br/><br/>Destination:<br/>`http://`<br/>`https://` |
| `createDownloadResumable` | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file:///` | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file://` |
> On Android **no scheme** defaults to a bundled resource.
## Permissions
### Android
The following permissions are added automatically through this library's **AndroidManifest.xml**.
### iOS
_No permissions required_.