Save a file to device folder (like "Download")

Hi, all, I wrote a tutorial about how to save remote content to the local file system using Expo.

I also published a package on npm which handles all this for you and sends a nice notification to the user with download progress information, so the user’s not left wondering whether the download is still going or failed or finished, etc.

Cheers!

4 Likes

It is great. Thanks. :innocent: :100:

1 Like

Thanks for the tutorial @farhansayshi, I’m using the code to download a mp3/mp4 file and it worked perfectly on Android but unfortunately, it’s not working on IOS. I have this error popping up:
Error: Asset couldn’t be saved to photo library]

Here is my code:

const downloadFileHandler = async (uri) =>{

    let fileUri = FileSystem.documentDirectory + test.mp4';

    try {
        const res = await FileSystem.downloadAsync(uri, fileUri)
        saveFile(res.uri)
    }   catch(err){
        console.log("FS Err: ", err)
    }}

const saveFile = async (fileUri) => {
    const { status } = await Permissions.askAsync(Permissions.CAMERA_ROLL);
    if (status === "granted") {
        try{
            const asset =  await MediaLibrary.createAssetAsync(fileUri);
            const album = await MediaLibrary.getAlbumAsync('Download');
            if (album == null) {
                await MediaLibrary.createAlbumAsync('Download', asset, false);
            } else {
                await MediaLibrary.addAssetsToAlbumAsync([asset], album, false);
            }
            alert("Success, file was successfully downloaded!");

        }catch(err){
            console.log("Save err: ", err)
        }
      
    }
  }

I’m in the process of finding a solution, if you have any suggestion please tell me.

Thanks in advance!

Hi,

I can confirm that this doesn’t seem to be working on mp3/mp4 files on iOS.

iOS isn’t my forte, but I’ll take a look at this and get back to you. Thanks for bringing this to my attention. Will reply back here when I have more news.

Hello,

Exactly, even though it is downloading images, but doesn’t work for audio/video assets.

I guess that we have to change something in the MediaLibrary(the asset variable) because the error is prompting there.

I’m looking forward to finding a solution as well, also It would be a good idea to make a tutorial on the expo-file-dl, thanks!

I’ve found out how to save mp3/mp4 files.

On iOS instead of using the MediaLibrary for these files (at the moment MediaLibrary only accepts images on iOS), one should use Sharing.shareAsync(url, options) from the expo-sharing library.

Here’s some rough code which can work to handle mp3/mp4 files on iOS (with a little creativity you can support all audio/video files on iOS this way as well):

const ios = Platform.OS === 'ios'

try {
        const res = await FileSystem.downloadAsync(uri, fileUri)
        if (ios) {
            if (fileUri.endsWith('mp3')) {
                const shareResult = await Sharing.shareAsync(fileUri, { UTI: 'public.audio' });
                return;
            } else if (fileUri.endsWith('mp4')) {
                const shareResult = await Sharing.shareAsync(fileUri, { UTI: 'public.movie' });
                return;
            }
        }
        saveFile(res.uri)
}

For the UTI option I also found public.mp3 and public.mp4 to work as well. This will trigger a popup from the system that allows the user to choose to save the file to their device and to choose the folder where they want to save the file to (I tested this on an iPad running iOS 14.1).

Note that this method makes it not very useful to specify the folder to download the file to (as the user in the end chooses the folder they wish to download the file to). It also does not work if the user leaves the app while the file is downloading (because the share prompt is not accessible outside the app). Things to keep in mind while developing.

I’m going to work on adding this to expo-file-dl. Did you find the README there not to be helpful? There’s also example code which shows how to use the expo-file-dl package here: expo-file-dl-example

Thank you @farhansayshi! I tried this way it worked perfectly.

as for the expo-file-dl-example, went through the code but found some difficulties because I didn’t learn Typescript yet. I also forgot about the README.md, I will go through it and get back to you with my response, but my thoughts are that this package will only work on Android because the download process differs from one device to another.

thanks in advance!

Yeah, I am working on adding this method to the package so that the download will happen on both Android & iOS for all media.

Right now it seems like downloads for image files are the same on both platforms, but audio/video/pdf/etc. are different on iOS. The plan is to add this code in to handle those media types correctly and update the README to point out this difference.

Thanks again for bringing this to my attention :slight_smile:

1 Like

No worries and yes, it would be a great idea to have it similar on both platforms.

Keep up the good work!

I am using media library to save the file into user storage, it works perfect on some android devices but some android devices gives error , unable to copy file into internal storage

What should i do ??
Please help me, and thanks in advance

I am using bare react native

More information would be very helpful. Which devices do you see this error on? What version of Android are they running? Do any of them have full storage, etc.?

Thanks for this awesome library . I am really happy to integrate it in my application.

Can you help me with my bare project integration

Currently my app.json looks like this

{
  "name": "demo_app",
  "displayName": "demo_app"
}

But the readme guide shows me to add something like this

{
  "expo": {
    ...
    "android": {
      ...
      "useNextNotificationsApi": true,
    }
  }
}

I am bit confused about this app.json structure . Can you help me out and tell me the exact code structure of app.json for bare react-native project.

Glad to hear you are finding it useful, @mantu728 !

There’s actually a reference implementation I put on GitHub. You can see the app.json file for a bare react native app that uses the library here: expo-file-dl-example/app.json at bare · kathawala/expo-file-dl-example · GitHub

Basically you want the following in your app.json:

{
  "name": "demo_app",
  "displayName": "demo_app",
  "expo": {
    "amdroid": {
      "useNextNotificationsApi": true
    }
  }
}

I hope that helps!

Thanks for the quick response, sir. It helped.

I have updated the library and blog post linked in the OP. So, for anyone coming across this now, it is current and works with Expo SDK 40 and all file types for both iOS and Android