This is documentation for the next SDK version. For up-to-date documentation, see the latest version (SDK 53).

Expo FileSystem (legacy) iconExpo FileSystem (legacy)

A library that provides access to the local file system on the device.

Android
iOS
tvOS

The legacy version of the FileSystem API is included in the expo-file-system library. It can be used alongside the modern API for backward compatibility reasons.

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.

Diagram explaining how expo-file-system interacts with different resources
How expo-file-system works differently inside of the Expo Go app

Within Expo Go, each project has a separate file system scope and has no access to the file system of other projects.

Installation

Terminal
npx expo install expo-file-system

If you are installing this in an existing React Native app, make sure to install expo in your project.

Usage

Downloading files

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

Giphy example
import * as FileSystem from 'expo-file-system/legacy'; 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 export async function addMultipleGifs(gifIds: string[]) { 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 export async function getSingleGif(gifId: string) { 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 export async function getGifContentUri(gifId: string) { return FileSystem.getContentUriAsync(await getSingleGif(gifId)); } // Deletes whole giphy directory with all its content export async function deleteAllGifs() { 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:

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

import * as FileSystem from 'expo-file-system/legacy';

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:

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 method.

No API data file found, sorry!

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 nameAndroidiOS
getInfoAsyncfile:///,
content://,
asset://,
no scheme
file://,
ph://,
assets-library://
readAsStringAsyncfile:///,
asset://,
SAF URI
file://
writeAsStringAsyncfile:///,
SAF URI
file://
deleteAsyncfile:///,
SAF URI
file://
moveAsyncSource:
file:///,
SAF URI

Destination:
file://
Source:
file://

Destination:
file://
copyAsyncSource:
file:///,
content://,
asset://,
SAF URI,
no scheme

Destination:
file://
Source:
file://,
ph://,
assets-library://

Destination:
file://
makeDirectoryAsyncfile:///file://
readDirectoryAsyncfile:///file://
downloadAsyncSource:
http://,
https://

Destination:
file:///
Source:
http://,
https://

Destination:
file://
uploadAsyncSource:
file:///

Destination:
http://
https://
Source:
file://

Destination:
http://
https://
createDownloadResumableSource:
http://,
https://

Destination:
file:///
Source:
http://,
https://

Destination:
file://

On Android no scheme defaults to a bundled resource.

Permissions

Android

The following permissions are added automatically through this library's AndroidManifest.xml.

Android PermissionDescription

READ_EXTERNAL_STORAGE

Allows an application to read from external storage.

WRITE_EXTERNAL_STORAGE

Allows an application to write to external storage.

INTERNET

Allows applications to open network sockets.

iOS

No permissions required.