Social Media, Tutorials, PHP & MySQL

Recently I've been looking into Dropbox's API so I decided to have a go at building an application that can upload a file or an entire folder directly to Dropbox.

In order to follow along you will need to create a App on Dropbox https://www.dropbox.com/developers/apps/create

  • Create a Dropbox API App - this will allow access to the Core API
  • The type should be Files and datastores
  • Limit the app to it's own folder
  • Give the app a unique name

Once the app has been created click on Generate access token and save that, it will be used later. User authentication won't be needed for this application.

Now the Dropbox app has been created the Core API SKD for PHP will be needed, it can be downloaded from https://www.dropbox.com/developers/core/sdks/php, In this guide I'll be using composer instead of manually downloading it.

PHP Application

The php application is now ready to be build create a project folder, I will call mine dropboxbckup.

In your project folder create these files:

  • backup.php
  • composer.json
  • index.php

Open composer.json and put:

{
    "require": {
        "php": ">=5.3.0",
        "dropbox/dropbox-sdk": "1.1.*"
    }
}

Next open a command prompt/terminal and navigate to the project and enter composer install, this will install all dependencies needed, in this case it will download the Dropbox SKD into a vendor directory.

If you don't have composer installed you won't be able to follow the above step, in which case install composer: Install Composer instructions: https://getcomposer.org/doc/00-intro.md#globally

Next open index.php, load the vendor autoload file if it exists, if it does not then print out an error:

<?php
if(file_exists('vendor/autoload.php')){
    require 'vendor/autoload.php';


} else {
    echo "<h1>Please install via composer.json</h1>";
    echo "<p>Install Composer instructions: <a href='https://getcomposer.org/doc/00-intro.md#globally'>https://getcomposer.org/doc/00-intro.md#globally</a></p>";
    echo "<p>Once composer is installed navigate to the working directory in your terminal/command prompt and enter 'composer install'</p>";
    exit;
}

After requiring the autoload class, add another require for backup.php this will be the class that does that use's Dropbox's API

Create a variable called $token and enter your access token (generated by Dropbox when you created the app). also create $project and $projectFolder variables.

$project should contain the project name and a version number
$projectFolder - a folder where all uploads are stored in the app/dropboxapp/foldername this way you can use the same app for multiple servers and know which project each folder is.

require 'vendor/autoload.php';
require 'backup.php';    

//set access token
$token = 'Your access token here';
$project = 'dropboxbackup/1.0';
$projectFolder = 'localserver';

Next create a new instance of a backup class and pass it the token, project and projectfolder. The next line (commented out) tells the script what you want to upload, either a file of a folder.

$bk = new Backup($token,$project,$projectFolder);
//$bk->upload('index.php');

completed index.php:

<?php
if(file_exists('vendor/autoload.php')){
    require 'vendor/autoload.php';
    require 'backup.php';    

    //set access token
    $token = 'Your access token here';
    $project = 'dropboxbackup/1.0';
    $projectFolder = 'localserver';

    $bk = new Backup($token,$project,$projectFolder);
    //$bk->upload('index.php');

    echo 'Upload Complete';

} else {
    echo "<h1>Please install via composer.json</h1>";
    echo "<p>Install Composer instructions: <a href='https://getcomposer.org/doc/00-intro.md#globally'>https://getcomposer.org/doc/00-intro.md#globally</a></p>";
    echo "<p>Once composer is installed navigate to the working directory in your terminal/command prompt and enter 'composer install'</p>";
    exit;
}

backup.php

Next open backup.php.

The class name should always be the same as the file name:

class Backup {

Next create 2 private properties, $dbxClient and $projectFolder.
$dbxClient will be the Dropbox object to interact with the API

Create a construct method this will collect the token project and projectFolder from the index.php page. a new instance of the Dropbox Client class will be created using the token to authenticate your access and project to tell the API the name and version of the project.

private $dbxClient;
private $projectFolder;

public function __construct($token,$project,$projectFolder){
    $this->dbxClient = new Dropbox\Client($token, $project);
    $this->projectFolder = $projectFolder;
}

Next a method called upload will be created, this will expect $dirtocopy, this is either a file or folder.

If the file or folder does not exist the script will quit with a message.
if $dirtocopy is a file it will be passed to a uploadFile method otherwise a new RecursiveIteratorIterator is created to collect all folders and files, ignoring files starting with dots and ignoring any files or folders that don't have permission.
Then the files are looped through and checked against an ignore list, if no match is found the file is passed to the uploadFile method.

public function upload($dirtocopy){

    if(!file_exists($dirtocopy)){

        exit("File $dirtocopy does not exist");
        
    } else {

        //if dealing with a file upload it
        if(is_file($dirtocopy)){
            $this->uploadFile($dirtocopy);
               
        } else { //otherwise collect all files and folders

            $iter = new \RecursiveIteratorIterator(
                new \RecursiveDirectoryIterator($dirtocopy, \RecursiveDirectoryIterator::SKIP_DOTS),
                \RecursiveIteratorIterator::SELF_FIRST,
                \RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore "Permission denied"
            );

            //loop through all entries
            foreach($iter as $file) {

                $words = explode('/',$file);
                $stop = end($words);    

                //if file is not in the ignore list pass to uploadFile method
                if(!in_array($stop, $this->ignoreList())){
                    $this->uploadFile($file);
                }

            }
        }
    }
}

public function ignoreList(){
    return array(
        '.DS_Store',
        'cgi-bin'
    );
}

The uploadFile method collects the file and passed it to Dropbox's uploadFile method passing in the project folder name followed by the file.

Each file uploaded will be unique, if a file already exists with the same name a number will be append to the filename.

To have Dropbox replace the existing file open vendor/dropbox/dropbox-sdk/lib/Dropbox/WriteMode.php on line 48 replace false to true on the overwrite option:

static function add()
{
    if (self::$addInstance === null) {
        self::$addInstance = new WriteMode(array("overwrite" => "true"));
    }
    return self::$addInstance;
}
public function uploadFile($file){
    $f = fopen($file, "rb");
    $this->dbxClient->uploadFile("/".$this->projectFolder."/$file", Dropbox\WriteMode::add(), $f);
    fclose($f);
}

Complete backup.php:

<?php

class Backup {

    private $dbxClient;
    private $projectFolder;

    /**
     * __construct pass token and project to the client method
     * @param string $token  authorization token for Dropbox API 
     * @param string $project       name of project and version
     * @param string $projectFolder name of the folder to upload into
     */
    public function __construct($token,$project,$projectFolder){
        $this->dbxClient = new Dropbox\Client($token, $project);
        $this->projectFolder = $projectFolder;
    }

    /**
     * upload set the file or directory to upload
     * @param  [type] $dirtocopy [description]
     * @return [type]            [description]
     */
    public function upload($dirtocopy){

        if(!file_exists($dirtocopy)){

            exit("File $dirtocopy does not exist");
            
        } else {

            //if dealing with a file upload it
            if(is_file($dirtocopy)){
                $this->uploadFile($dirtocopy);
                   
            } else { //otherwise collect all files and folders

                $iter = new \RecursiveIteratorIterator(
                    new \RecursiveDirectoryIterator($dirtocopy, \RecursiveDirectoryIterator::SKIP_DOTS),
                    \RecursiveIteratorIterator::SELF_FIRST,
                    \RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore "Permission denied"
                );

                //loop through all entries
                foreach($iter as $file) {

                    $words = explode('/',$file);
                    $stop = end($words);    

                    //if file is not in the ignore list pass to uploadFile method
                    if(!in_array($stop, $this->ignoreList())){
                        $this->uploadFile($file);
                    }

                }
            }
        }
    }

    /**
     * uploadFile upload file to dropbox using the Dropbox API
     * @param  string $file path to file
     */
    public function uploadFile($file){
        $f = fopen($file, "rb");
        $this->dbxClient->uploadFile("/".$this->projectFolder."/$file", Dropbox\WriteMode::add(), $f);
        fclose($f);
    }

    /**
     * ignoreList array of filenames or directories to ignore
     * @return array 
     */
    public function ignoreList(){
        return array(
            '.DS_Store',
            'cgi-bin'
        );
    }
}