Search code examples
androidcordovavue.jscordova-pluginsquasar-framework

Cordova: How to move file to the Download folder?


THE SITUATION:

In my mobile app I need to download a file and store in the Download folder.

The download part is working fine. The file is properly downloaded from the server and stored in the following folder:

file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/my_file.pdf

But the location is not really user-friendly.

To access it I have to go to: Internal storage / Android / data / org.cordova.MY_APP_NAME.app /

So I need to move it to the main Download folder.

The file transfer is what I don't manage to do.

I know that there are already several similar questions on SO.

I have tried them all but none really worked for me, I could never see the file in the actual Download folder.

PROJECT INFO:

I am using Quasar with Vuejs and Cordova.

PLATFORM:

For the moment I am working with Android. But ideally I am looking for a solution that works for both Android and IOS.

THE CODE:

The download code:

var fileTransfer = new FileTransfer() // eslint-disable-line
var uri = encodeURI('https://MY_SERVER_PATH')

fileTransfer.download(
  uri,
  cordova.file.externalApplicationStorageDirectory + 'my_file.pdf',
  entry => {
    console.log('download complete: ' + entry.toURL())
    this.moveFile(entry.toURL())
  },
  error => {
    console.log('download error source ' + error.source)
    console.log('download error target ' + error.target)
    console.log('download error code' + error.code)
  },
  false,
  {
    headers: {
      'Authorization': 'Basic asdasdasdasdassdasdasd'
    }
  }
)

The File transfer code:

moveFile(fileUri) {
  window.resolveLocalFileSystemURL(
    fileUri,
    fileEntry => {
      let newFileUri = 'file:///storage/emulated/0/Download'

      window.resolveLocalFileSystemURL(
        newFileUri,
        dirEntry => {
          fileEntry.moveTo(dirEntry, 'new_filename.pdf', this.moveFileSuccess, this.moveFileError)
        },
        this.moveFileError)
    },
    this.moveFileError)
},
moveFileSuccess(entry) {
  console.log('file move success')
  console.log(entry)
},
moveFileError(error) {
  console.log('file move error')
  console.log(error)
}

THE QUESTION:

How can I move a file to the Download folder?

Thanks

EDIT:

This is the console log of the cordova.file object:

applicationDirectory: "file:///android_asset/"
applicationStorageDirectory: "file:///data/user/0/org.cordova.MY_APP_NAME.app/"
cacheDirectory:"file:///data/user/0/org.cordova.MY_APP_NAME.app/cache/"
dataDirectory: "file:///data/user/0/org.cordova.MY_APP_NAME.app/files/"
documentsDirectory: null
externalApplicationStorageDirectory: "file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/"
externalCacheDirectory: "file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/cache/"
externalDataDirectory: "file:///storage/emulated/0/Android/data/org.cordova.MY_APP_NAME.app/files/"
externalRootDirectory: "file:///storage/emulated/0/"
sharedDirectory: null
syncedDataDirectory: null
tempDirectory: null

Solution

  • Okay I managed to resolve it.

    • First of all is totally unnecessary to download and then move the file. It can just be directly downloaded in the desired direction.

    • The correct path (in my case) was this:

      cordova.file.externalRootDirectory + 'download/' + 'my_file.pdf

      that correspond to: file:///storage/emulated/0/download/my_file.pdf

      and that means that to find the file inside the device you have to go to: Internal Storage / Download / my_file.pdf

    • Add the following value in the config.xml:

      <preference name="AndroidPersistentFileLocation" value="Compatibility" /> <preference name="AndroidExtraFilesystems" value="files,files-external,documents,sdcard,root" />

    • It's important to check for permission using this cordova plugin: cordova-plugin-android-permissions

    You can make a quick test like this:

    let permissions = cordova.plugins.permissions
    permissions.checkPermission(permissions.READ_EXTERNAL_STORAGE, checkPermissionCallback, null)
    
    function checkPermissionCallback(status) {
      console.log('checking permissions')
      console.log(status)
    }
    

    Most probably the result is false. And that means that we have to request permission to the user:

    permissions.requestPermission(successCallback, errorCallback, permission)
    

    In this way it will appear the alert asking for permission.

    THE CODE:

    To put it all together, this is the working code:

    let pdfPath = 'https://MY_SERVER_PATH'
    
    let permissions = cordova.plugins.permissions
    permissions.checkPermission(permissions.READ_EXTERNAL_STORAGE, checkPermissionCallback, null)
    
    // Checking for permissions
    function checkPermissionCallback(status) {
      console.log('checking permissions')
      console.log(status)
      if (!status.hasPermission) {
        var errorCallback = function () {
          console.warn('Storage permission is not turned on')
        }
        // Asking permission to the user
        permissions.requestPermission(
          permissions.READ_EXTERNAL_STORAGE,
          function (status) {
            if (!status.hasPermission) {
              errorCallback()
            } else {
              // proceed with downloading
              downloadFile()
            }
          },
          errorCallback)
      } else {
        downloadFile()
      }
    }
    
    function downloadFile() {
      let filePath = cordova.file.externalRootDirectory + 'download/' + 'my_file.pdf'
      let fileTransfer = new window.FileTransfer()
      let uri = encodeURI(decodeURIComponent(pdfPath))
    
      // Downloading the file
      fileTransfer.download(uri, filePath,
        function (entry) {
          console.log('Successfully downloaded file, full path is ' + entry.fullPath)
          console.log(entry)
        },
        function (error) {
          console.log('error')
          console.log(error)
        },
        false
      )
    }