Fail to save file in Android Device in React Native App using Expo File System

Viewed 310

all, I am new to React Native and Expo File System. I am now having an issue when I was trying to save the document I downloaded from an URL. The error I am facing is: [Error: Location 'content://com.android.providers.downloads.documents/tree/downloads' isn't writable.], which happened when the 'saveAndroidFile' function is called. Here is my code:

function myScreen () {

const [downloadProgress, setDownloadProgress] = React.useState()
  const downloadPath =
    FileSystem.documentDirectory +
    (Platform.OS == 'android' ? '' : '')

  const ensureDirAsync = async (dir, intermediates = true) => {
    const props = await FileSystem.getInfoAsync(dir)
    if (props.exist && props.isDirectory) {
      return props
    }
    let _ = await FileSystem.makeDirectoryAsync(dir, { intermediates })
    return await ensureDirAsync(dir, intermediates)
  }
  const downloadCallback = (downloadProgress) => {
    const progress =
      downloadProgress.totalBytesWritten /
      downloadProgress.totalBytesExpectedToWrite
    setDownloadProgress(progress)
  }

  const downloadFile = async (item) => {
    
    const fileUrl = `whateverpath/Doc111.pdf`

    if (Platform.OS == 'android') {
      const dir = ensureDirAsync(downloadPath)
    }

    // get file name without extension and the extension
    const list = fileUrl.split('/')
    let fileNameWithExtension = list[list.length - 1]
    let fileName = fileNameWithExtension.split('.')[0]
    const fileType = fileNameWithExtension.split('.')[1]
    
    const downloadResumable = FileSystem.createDownloadResumable(
      fileUrl,
      downloadPath + fileName,
      {},
      downloadCallback
    )

    try {
      const { uri } = await downloadResumable.downloadAsync()
      if (Platform.OS == 'android') saveAndroidFile(uri, fileName, fileType)
      // else saveIosFile(uri)
    } catch (e) {
      console.error('download error:', e)
    }
  }

  const saveAndroidFile = async (fileUri, fileName = 'File', fileType) => {
  
    // find the mimeType for the downloading file
    // memeTypes is a constant store the mapping relationship
    const mimeType = mimeTypes[fileType]

    try {
      const fileString = await FileSystem.readAsStringAsync(fileUri, {
        encoding: FileSystem.EncodingType.Base64,
      })

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

      try {
        await StorageAccessFramework.createFileAsync(
          permissions.directoryUri,
          fileName,
          mimeType
        )
          .then(async (uri) => {
            await FileSystem.writeAsStringAsync(uri, fileString, {
              encoding: FileSystem.EncodingType.Base64,
            })
            alert('Report Downloaded Successfully')
          })
          .catch((e) => {
            
            console.log('save error', e)
          })
      } catch (e) {
        throw new Error(e)
      }
    } catch (err) {}
  }
  
  return (
    <View >
      <Text style={[{ width: '60%' }]}>
        Doc111.pdf
      <Text>

      <TouchableOpacity onPress={() => downloadFile()}>
        <AntDIcon name='download' size={14} />
      </TouchableOpacity>
    </View>
  )

}

Just mentioned here, the folder I have no write access is the "Downloads" folder. And I think this is the location "FileSystem.documentDirectory" will by default take the app download to. And after I change the location, like switch the location to some internal storage folder, for example device_internal_storage/whatever location, I can download the file.

So I am thinking, should I change "FileSystem.documentDirectory" to make it download to somewhere else, or should I find a way to allow the write access to the "Downloads" folder. But I cannot figure out how can I do either of these, if anyone can help to solve these two issue or have any other solution, I would appreciate that. Thank you

0 Answers
Related