This documentation is available as Markdown for AI agents and LLMs. See the full Markdown index or append .md to any documentation URL.
Expo FileSystem (legacy)
A library that provides access to the local file system on the device.
Android
iOS
tvOS
Included in Expo Go
Recommended version:
~56.0.11
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.
constcallback=downloadProgress=>{const progress = downloadProgress.totalBytesWritten/ downloadProgress.totalBytesExpectedToWrite;this.setState({downloadProgress: progress,});};const downloadResumable =FileSystem.createDownloadResumable('https://example.com/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 =awaitAsyncStorage.getItem('pausedDownload');const downloadSnapshot =JSON.parse(downloadSnapshotJson);const downloadResumable =newFileSystem.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*asFileSystemfrom'expo-file-system/legacy';const gifDir =FileSystem.cacheDirectory+'giphy/';constgifFileUri=(gifId: string)=> gifDir +`gif_${gifId}_200.gif`;constgifUrl=(gifId: string)=>`https://media1.giphy.com/media/${gifId}/200.gif`;// Checks if gif directory exists. If not, creates itasyncfunctionensureDirExists(){const dirInfo =awaitFileSystem.getInfoAsync(gifDir);if(!dirInfo.exists){console.log("Gif directory doesn't exist, creating…");awaitFileSystem.makeDirectoryAsync(gifDir,{intermediates:true});}}// Downloads all gifs specified as array of IDsexportasyncfunctionaddMultipleGifs(gifIds: string[]){try{awaitensureDirExists();console.log('Downloading', gifIds.length,'gif files…');awaitPromise.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 itexportasyncfunctiongetSingleGif(gifId: string){awaitensureDirExists();const fileUri =gifFileUri(gifId);const fileInfo =awaitFileSystem.getInfoAsync(fileUri);if(!fileInfo.exists){console.log("Gif isn't cached locally. Downloading…");awaitFileSystem.downloadAsync(gifUrl(gifId), fileUri);}return fileUri;}// Exports shareable URI - it can be shared outside your appexportasyncfunctiongetGifContentUri(gifId: string){returnFileSystem.getContentUriAsync(awaitgetSingleGif(gifId));}// Deletes whole giphy directory with all its contentexportasyncfunctiondeleteAllGifs(){console.log('Deleting all GIF files…');awaitFileSystem.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*asFileSystemfrom'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.