Dropbox direct upload files from browser

13,152

Solution 1

Many thanks to @smarx with his pointers I was able to reach the final solution.

Also I have added a few extra features like listening to upload progress so that the users can be showed with the upload progress percentage.

HTML

<input type="file" name="file" id="file" onchange="doUpload(event)">

JavaScript

var doUpload = function(event){

      var input = event.target;
      var reader = new FileReader();


      reader.onload = function(){
        var arrayBuffer = reader.result;
        var arrayBufferView = new Uint8Array( arrayBuffer );
        var blob = new Blob( [ arrayBufferView ], { type: input.files[0].type } );
        var urlCreator = window.URL || window.webkitURL;
        var imageUrl = urlCreator.createObjectURL( blob );   

        $.ajax({  
          url: "https://api-content.dropbox.com/1/files_put/auto/YourDirectory/" + input.files[0].name,  
          headers: {  
            'Authorization':'Bearer ' +YourToken,  
            'Content-Length':input.files[0].size  
          },  
          crossDomain: true,  
          crossOrigin: true,  
          type: 'PUT',  
          contentType: input.files[0].type,  
          data: arrayBuffer,  
          dataType: 'json',  
          processData: false,
          xhr: function()
          {
            var xhr = new window.XMLHttpRequest();
           //Upload progress, litsens to the upload progress 
           //and get the upload status
           xhr.upload.addEventListener("progress", function(evt){
            if (evt.lengthComputable) {
              var percentComplete = parseInt( parseFloat(evt.loaded / evt.total) * 100);
              //Do something with upload progress
              $('#uploadProgress').html(percentComplete);
              $('#uploadProgressBar').css('width',percentComplete+'%');
             }
            }, false);
           },
         beforeSend: function(){
           // Things you do before sending the file 
           // like showing the loader GIF
         },
         success : function(result) {
           // Display the results from dropbox after upload 
           // Other stuff on complete
          },

        }); 
       }
     reader.readAsArrayBuffer(input.files[0]);
    }

U have used the PUT method as our only objective is to upload files,As per my studies on various resources ( StackOverflow and zacharyvoase ) A put method can stream large files, also its desigend to put files on a specified URI , if file exist the file must be replaced. A PUT method cannot be moved to a different URL other than the URL Specified.

The Risk

You are at risk by using access token at client side, there needs to be high security measures to mask the token. But modern Web dev tools like Browser consoles , Firebug etc can monitor your server requests and can see your access token.

Solution 2

Dropbox just posted a blog with instructions on how to do this. You can find it at https://blogs.dropbox.com/developers/2016/03/how-formio-uses-dropbox-as-a-file-backend-for-javascript-apps/ (Full disclosure, I wrote the blog post.)

Here is how to upload a file.

/**
 * Two variables should already be set.
 * dropboxToken = OAuth token received then signing in with OAuth.
 * file = file object selected in the file widget.
 */

var xhr = new XMLHttpRequest();

xhr.upload.onprogress = function(evt) {
    var percentComplete = parseInt(100.0 * evt.loaded / evt.total);
    // Upload in progress. Do something here with the percent complete.
};

xhr.onload = function() {
    if (xhr.status === 200) {
        var fileInfo = JSON.parse(xhr.response);
        // Upload succeeded. Do something here with the file info.
    }
    else {
        var errorMessage = xhr.response || 'Unable to upload file';
        // Upload failed. Do something here with the error.
    }
};

xhr.open('POST', 'https://content.dropboxapi.com/2/files/upload');
xhr.setRequestHeader('Authorization', 'Bearer ' + dropboxToken);
xhr.setRequestHeader('Content-Type', 'application/octet-stream');
xhr.setRequestHeader('Dropbox-API-Arg', JSON.stringify({
    path: '/' +  file.name,
    mode: 'add',
    autorename: true,
    mute: false
}));

xhr.send(file);

Then to download a file from dropbox do this.

var downloadFile = function(evt, file) {
  evt.preventDefault();
  var xhr = new XMLHttpRequest();
  xhr.responseType = 'arraybuffer';

  xhr.onload = function() {
    if (xhr.status === 200) {
      var blob = new Blob([xhr.response], {type: ’application/octet-stream’});
      FileSaver.saveAs(blob, file.name, true);
    }
    else {
      var errorMessage = xhr.response || 'Unable to download file';
      // Upload failed. Do something here with the error.
    }
  };

  xhr.open('POST', 'https://content.dropboxapi.com/2/files/download');
  xhr.setRequestHeader('Authorization', 'Bearer ' + dropboxToken);
  xhr.setRequestHeader('Dropbox-API-Arg', JSON.stringify({
    path: file.path_lower
  }));
  xhr.send();
}

FileSaver and Blob will not work on older browsers so you could add a workaround to them.

As other answers have noted, each session uploading or downloading the file will need to have access to a dropbox token. Sending someone else's token to a user is a security issue since having the token will give them complete control over the dropbox account. The only way to make this work is to have each person authenticate with Dropbox and get their own token.

At Form.io we've implemented both the authentication and the upload/download into our platform. This makes it really easy to build web apps with dropbox as a backend for files.

Solution 3

"I need the files to be uploaded to my account and the clients need not login to dropbox."

Then you'll really need to do the upload server-side. To do it client side would mean sending the access token to the browser, at which point any user of your app could use that access token to do whatever they wanted with your account. (E.g. delete all the other files, upload their private DVD collection, etc.)

For security reasons, I would strongly recommend doing the upload server-side where you can keep the access token a secret.

Solution 4

The answers given so far don't utilize the Dropbox javascript SDK which I think would prob be the best way to go about it. Check out this link here:

https://github.com/dropbox/dropbox-sdk-js/blob/master/examples/javascript/upload/index.html

which provides an example which is ofc dependent on having downloaded the SDK. (Edit: after playing with SDK I realize that it creates a POST request similar to the accepted answer in this thread. However something the popular answer omits is the presence of an OPTIONS preflight call that the sdk makes prior to the actual POST)

I might also add that something that is not shown in the dropbox sdk examples is that you can upload a blob object to dropbox; this is useful for instance if you want to dynamically extract images from a canvas and upload them and don't want to upload something that has been selected from the file system via the file uploaded input.

Here is a brief example of the scenario I'm describing:

//code below after having included dropbox-sdk-js in your project.  
//Dropbox is in scope!
var dbx = new Dropbox.Dropbox({ accessToken: ACCESS_TOKEN });
//numerous stack overflow examples on creating a blob from data uri
var blob = dataURIToBlob(canvas.toDataUrl());
//the path here is the path of the file as it will exist on dropbox.
//should be unique or you will get a 4xx error
dbx.filesUpload({path: `unq_filename.png`, contents: blob})
Share:
13,152
Clain Dsilva
Author by

Clain Dsilva

I’m a Geek Software engineer, Web Developer, SEO specialist, with hand full experience in: HTML5, CSS, JavaScript, Ajax, jQuery MooTools, PHP-MySQL and SEO I have been into online platforms since 15 years and also work as SEO Specialist for many of my personal clients. I have a great passion towards PHP and Open source movement and love to experiment on the web.

Updated on June 21, 2022

Comments

  • Clain Dsilva
    Clain Dsilva about 2 years

    I am trying to upload files directly to dropbox [from a browser / web application], The "uploadFile" function on the code API needs the file to be uploaded available on the server, this puts me in trouble, because I do not want any files to be uploaded to my server and from there to dropbox.

    $f = fopen("test.jpg", "rb"); // requires file on server
    $result = $dbxClient->uploadFile("test.jpg", dbx\WriteMode::add(), $f);
    fclose($f);
    

    Tried out this https://github.com/dropbox/dropbox-js disappointed to say that there is no clear documentation, many of the links on the documentation part is broken.

    I need the files to be uploaded to my account and the clients need not login to dropbox.

    Any pointers would be really appreciated. looking for Ajax / JavaScript methods.

    Update

    I have tried the following, but no response from Dropbox

    HTML

    <input type="file" name="file" id="file" onchange="doUpload(event)">
    

    JavaScript

    var doUpload = function(event){
    
    var input = event.target;
    var reader = new FileReader();
    
    
      reader.onload = function(){
        var arrayBuffer = reader.result;
    
       $.ajax({  
        url: "https://api-content.dropbox.com/1/files_put/auto/uploads/" + input.files[0].name,  
        headers: {  
            Authorization: 'Bearer ' + MyAccessToken,  
            contentLength: file.size  
        },  
        crossDomain: true,  
        crossOrigin: true,  
        type: 'PUT',  
        contentType: input.files[0].type,  
        data: arrayBuffer,  
        dataType: 'json',  
        processData: false,
        success : function(result) {
            $('#uploadResults').html(result);
        }
        });
      }
     reader.readAsArrayBuffer(input.files[0]);
    }
    
  • Clain Dsilva
    Clain Dsilva over 8 years
    while its true to keep the access token a secret, its also not advisable to upload bigger files to the server[ > 100 mb] at first and from there to dropbox. Any method to directly upload files my dropbox account would be great.
  • Simon H
    Simon H almost 8 years
    Having researched that this evening, it looks as though S3 provide item by item signatures for uploads, which is much more secure than what handing out your token
  • stoic_monk
    stoic_monk over 6 years
    This would be a good answer but you need to include the fact that the Dropbox object is from the SDK. Also this is basically just a cut and paste of a documentation example.
  • Sunny_Sid
    Sunny_Sid over 5 years
    You have missed /(slash) here: dbx.filesUpload({path: '/filename.png', contents: file})