Tuesday, January 27, 2015

Dropbox API Continued: Download File

I knew it wasn't going to be long. Somehow I just knew my way of using a hard-coded Dropbox share link to pull a master manifest file would easily break when Dropbox decides to change the link to the file. The PART store uses Dropbox to host every file used. It uses a hardcoded link to a dropbox shared file that is what I call the master json manifest file. This file contains a list of all the apps that will be available in this PART store app.

With each app listed, it includes information on Android package names, root directory, and an app-specific json manifest file that shows where to download the apk files for each app, version numbers, upload whitelist/blacklist, etc. Basically each individual app-specific manifest gives the app developers in the PART research team control on what they allow their clients to do with it.

The problem with the above scheme is that Dropbox uses a hashed link for any shared files it hosts. Awhile back, they had public folders where you could access any folder as long as you know the Dropbox file system; there was no hashing in the links. They've since changed it and the hashing can change if you accidentally delete the file and reupload or some other variations. I first implemented this knowing this wouldn't last forever, but I just got lazy and figured downloading a file through HTTP is obviously the easiest. Well this morning as I was doing some clean up on my computer, I finally tasted the consequence of my laziness. The PART Store app stopped working. And the worst part was that there was no way of issuing a self update to fix it because the self-update sits on that master manifest file.

I had to come up with an alternative. Now that I have implemented Dropbox upload file API, I figure implementing the download file API would remove that problem as the API only requires the file structure of the folder and no hash links. In the even the file gets removed, I simply will have to restructure the file system back to the way it was and it should be fixed on a refresh in the app.

So using the downloading API is much like the upload API. You have to create a new class that extends the AsyncTask methods. Here I've created a class called DownloadFile:

package part.updater;

import android.content.Context;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

import com.dropbox.client2.DropboxAPI;
import com.dropbox.client2.exception.DropboxException;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;

public class DownloadFile extends AsyncTask<Void, Long, Boolean> {
    private Context mContext;
    private DropboxAPI<?> mApi;
    private String mFilePath;
    private File mFile;
    private FileOutputStream outputStream;

    public DownloadFile(Context context, DropboxAPI<?> api, String downloadFile, String localFile) {
        mContext = context;
        mApi = api;
        mFilePath = downloadFile;
        mFile = new File(localFile);
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            outputStream = new FileOutputStream(mFile);
            DropboxAPI.DropboxFileInfo info = mApi.getFile(mFilePath, null, outputStream, null);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (DropboxException e) {
            ((MyActivity)mContext).useBackupJSON();
            e.printStackTrace();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Boolean result) {
        ((MyActivity)mContext).readAppsJSON();
    }

}

The constructor takes three arguments; the context, the string path of the file to download on dropbox, and the string path of the file that you will download the file to. Very simple. The try catch clause catches exceptions when you specify a file that's not available on dropbox... or possibly your access token expires and something went wrong.

        try {
            //...
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (DropboxException e) {
            ((MyActivity)mContext).useBackupJSON();
            e.printStackTrace();
        }
In my case, I've added to connect to a backup server to pull a copy of the JSON file.

To use this, you'd do similar to what you did for file uploading.

if (isNetworkAvailable()) {
   DownloadFile masterJson = new DownloadFile(this, mDBApi, "/dropbox_path/apps.json", "/sdcard/apps.json");
   masterJson.execute();
}

No comments :

Post a Comment