Maxim Dantes
Maxim Dantes

Reputation: 33

Expo React Native, saving PDF files to Downloads folder

The following code works correctly for image files. But when I'm trying to save PDF file or other not-media formates, I get Could not create asset error.
I understand that expo-media-library is designed to work with media format files. Is there any alternative for expo-media-library to save other files formats?

import * as FileSystem from 'expo-file-system'
import * as Permissions from 'expo-permissions'
import * as MediaLibrary from 'expo-media-library'

const downloadFile = async (uri: string) => {
    const targetUri = FileSystem.documentDirectory + getFileName(uri)

    const downloadedFile = await FileSystem.downloadAsync(uri, targetUri)

    if (downloadedFile.status === 200) {
        if (Platform.OS === 'android') {
            const permission = await Permissions.askAsync(Permissions.MEDIA_LIBRARY)

            if (permission.status !== 'granted') {
                return
            }

            const asset = await MediaLibrary.createAssetAsync(downloadedFile.uri)
            const album = await MediaLibrary.getAlbumAsync('Download')

            await MediaLibrary.addAssetsToAlbumAsync([asset], album, false)
        }
    }
}

Upvotes: 3

Views: 13880

Answers (3)

QBKJ
QBKJ

Reputation: 1

For anyone else looking this is also what worked on my side for PDF-s:

import { Alert } from "react-native";
import * as IntentLauncher from "expo-intent-launcher";

export const handleOpenPdf = async (imageUrl) => {
  if (!imageUrl) {
    Alert.alert("...");
    return;
  }
  try {
    await IntentLauncher.startActivityAsync("android.intent.action.VIEW", {
      data: imageUrl,
      type: "application/pdf",
    });
  } catch (error) {
    console.error("Failed to open PDF:", error);
    Alert.alert("...");
  } finally {
//add logic
   }
};

Upvotes: 0

Uddhav Jadhav
Uddhav Jadhav

Reputation: 1

The permission can be persisted by storing the path. Here is how

Code to save/get the path from storage :

const savePermissionGrantedFolderURI = async (uri: string) => {
  await AsyncStorage.setItem("granted-permission-folder", uri);
};

export const getPermissionGrantedFolderURI = async () => {
  return await AsyncStorage.getItem("granted-permission-folder");
};

Code to check and get permission granted folder's uri :

const checkAndGetPermissionGrantedFolderURI = async() => {
  // get the uri if the permission is already granted
  const uri = await getPermissionGrantedFolderURI();

  if (uri) {
    // find if the uri is still valid
    try {
      const fileInfo = await FileSystem.StorageAccessFramework.readDirectoryAsync(uri);
      if (Array.isArray(fileInfo)) {
        return uri;
      }
    } catch (error) {
      // if not found or invalid, get the permission again by continuing
    }
  }

  // if not found or invalid, get the permission again
  const permissions =
    await FileSystem.StorageAccessFramework.requestDirectoryPermissionsAsync();

  if (permissions.granted) {
    // save the permission granted folder uri and return the uri
    await savePermissionGrantedFolderURI(permissions.directoryUri);
    return permissions.directoryUri;
  }

  // if permission is not granted
  return null;
};

Code to check platform and write file if its android :

const saveFileToDevice = async(
  uri: string, // or base64
  filename: string,
  mimetype: string
) => {
  if (Platform.OS === "android") {
    const permissionGrantedURI = await checkAndGetPermissionGrantedFolderURI();

    if (!permissionGrantedURI) return;

    // if you have base64 already skip this step
    const base64 = await FileSystem.readAsStringAsync(uri, {
      encoding: FileSystem.EncodingType.Base64,
    });

    const fileOnDeviceURI = await FileSystem.StorageAccessFramework.createFileAsync(
      permissionGrantedURI,
      filename,
      mimetype
    );

    await FileSystem.writeAsStringAsync(fileOnDeviceURI, base64, {
      encoding: FileSystem.EncodingType.Base64,
    });

    // if the file is saved successfully, return true
    return true;
  }

  await shareAsync(uri);
};

Upvotes: 0

RenishV8
RenishV8

Reputation: 186

it works on android device with https://docs.expo.dev/versions/latest/sdk/filesystem/#storageaccessframeworkcreatefileasyncparenturi-string-filename-string-mimetype-string

import * as FileSystem from 'expo-file-system';
import { StorageAccessFramework } from 'expo-file-system';

const permissions = await StorageAccessFramework.requestDirectoryPermissionsAsync();
if (!permissions.granted) {
    return;
}

try {
    await StorageAccessFramework.createFileAsync(permissions.directoryUri, fileName, 'application/pdf')
    .then((r) => {
        console.log(r);
    })
    .catch((e) => {
        console.log(e);
    });
} catch((e) => {
    console.log(e);
});

My pdf is well downloaded !

In my case i had to generate the file from a base64 string. My code :

import * as FileSystem from 'expo-file-system';
import { StorageAccessFramework } from 'expo-file-system';

const permissions = await StorageAccessFramework.requestDirectoryPermissionsAsync();
if (!permissions.granted) {
    return;
}

const base64Data = 'my base 64 data';

try {
    await StorageAccessFramework.createFileAsync(permissions.directoryUri, fileName, 'application/pdf')
        .then(async(uri) => {
            await FileSystem.writeAsStringAsync(uri, base64Data, { encoding: FileSystem.EncodingType.Base64 });
        })
        .catch((e) => {
            console.log(e);
        });
} catch (e) {
    throw new Error(e);
}

Upvotes: 5

Related Questions