Blog

How to Download a File Stored in Backendless With Your Android App

by on June 11, 2018

Since Backendless does not have native APIs to download files, today we’ll talk about how to implement this function in your Android application. In order to do that, we’ll need to combine the Backendless file listing API and android.app.DownloadManager. There are several alternatives to this approach, but the selected one requires less code to write and has a well-thought-out structure.

Once a developer uploads files to the Backendless Files system, each file gets a public URL that can either be obtained using Backendless Console or calculated using the following URL scheme:

https://api.backendless.com/<appId>/<rest-key>/files/<path>/<fileName>

publicURLThis public URI is the full path to the file in your Backendless file system. The directory listing API returns a list of the FileInfo objects representing the files located in the directory, where each element in the collection contains the following properties:

  • name – the name of the file or directory without any path information
  • public URL – absolute URL of the file or directory
  • URL – relative URL of the file or directory starting from the root of the file storage
  • created on – a timestamp indicating when the file or directory was created

In this guide, we’ll be dealing with listing and downloading all the files with the .html extension in the /web folder located in the root directory of your Backendless file system.

As for the Android app itself, we’ll need to:

  1. Define the INTERNET and WRITE_EXTERNAL_STORAGE  permissions in Android manifest:
    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  2. Create a method to list all files in the directory:
    public void filesListing(String path, String pattern)
    {
       Toast.makeText(this, "Loading files... Please wait", Toast.LENGTH_LONG).show();
       Backendless.Files.listing( path, pattern, false,
               new AsyncCallback<List<FileInfo>>()
       {
           @Override
           public void handleResponse( List<FileInfo> fileInfos )
           {
               for( FileInfo file:fileInfos)
               {
                   allFiles.add(file);
                   filesList.append("File name: " + file.getName()+ "\n" + "File path: " + file.getURL() + "\n\n");
               }
           }
           @Override
           public void handleFault( BackendlessFault backendlessFault )
           {
               Log.e("Listing error", "Error listing files: " + backendlessFault.getMessage());
           }
       });
    }
    

    In this code sample, allFiles is a variable that stores the results of the listing operation, and filesList is a TextView class where we show all the existing files.

    When the listing process is finished, we can start the download.

  3. Create a method that gets the public URIs of each file in a directory and will download these files in series:
    public void downloadFile(final FileInfo fileInfo) throws IOException
    {
       isStoragePermissionGranted();
       new Thread()
       {
           @Override
           public void run()
           {
               try
               {
                   Uri downloadUri = Uri.parse(fileInfo.getPublicUrl());
                   DownloadManager.Request request = new DownloadManager.Request(downloadUri);
                   DownloadManager downloadManager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
                   request.setDestinationInExternalPublicDir("/Download", fileInfo.getName());
                   request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
                   request.setVisibleInDownloadsUi(true);
                   downloadManager.enqueue(request);
               }
               catch(Exception e){
                   e.printStackTrace();
               }
           }
       }.start();
    }

    That’s all we need to make it work on Android devices with SDK < 23. On devices with SDK 23+, we’ll have to ask application users to grant permission to use their device storage. A method to check the device SDK and to ask for permission (if necessary) may look like this (in our code sample it is called before the download starts in Step 3):

    public  boolean isStoragePermissionGranted() {
       if (Build.VERSION.SDK_INT >= 23) {
           //permission is automatically granted on sdk<23 upon installation
           if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                   == PackageManager.PERMISSION_GRANTED) {
               Log.v("Permission status","Permission is granted");
               return true;
           } else {
               Log.v("Permission status","Permission is revoked");
               ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
               return false;
           }
       }
       else {
           Log.v("Permission status","Permission is granted");
           return true;
       }
    }

We need to call this method in the download method of our download activity class. The result of the user choice should be handled in a separate callback, the same callback in which the download process should be restored:

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
   super.onRequestPermissionsResult(requestCode, permissions, grantResults);
   if(grantResults[0]== PackageManager.PERMISSION_GRANTED){
       Log.v("Permission status","Permission: "+ permissions[0]+ "was "+ grantResults[0]);
       //resume tasks needing this permission
       downloadFiles.callOnClick();
   }
}

Where downloadFiles is a button to start the download.

The whole test app interface will look like this:
mainActivity
Selection_154
Once you click the ALLOW button, the download process will be started.
Selection_155
That’s it!

The archive with this application can be found here: https://drive.google.com/open?id=1A4Mx3UEWK_ckgaSjaw0TWU5OvVq4ocLY

Don’t forget to add your Backendless AppId and Android API key in the Defaults class. Thanks for reading this post, hope you like it! 

Leave a Reply