Saving files to Google Drive with Web Intents

Thursday, July 26, 2012 | 3:03 PM

Labels:

The Google Drive SDK opens up a great number of possibilities of integration with third-party applications and services. One such integration is via the new Web Intents API.

Web Intents lets web developers integrate their web apps with third-party services and avoid implementing the same feature with similar services over and over again. With Web Intents, a service can register itself to handle specific functionality, such as saving a file or sharing data. Client applications can then discover and interact with the service to use that specific functionality.

In this post, we’ll look at code for two example apps: a service that exposes a “save” web intent, and a client that consumes it to save files to Google Drive.

Exposing the Web Intent

To demonstrate web intents, we wrote a proof of concept app called Cloudfilepicker.com. This is a service that lets users or applications save and retrieve data from Drive via the Web Intents API. With Cloudfilepicker, any application that fires a “save” intent can save to Google Drive without directly implementing the API.

To build a service application like cloudfilepicker.com that interacts with Drive and Web Intents, create a Chrome app whose manifest exposes a http://webintents.org/save intent action. For example:

{
  "name": "Google Drive Web Intent",
  "version": "1",
  "app": {
    "launch": {
      "local_path": "index.html"
    }
  },
  "intents": {
    "http://webintents.org/save": [
      { 
        "type" : ["image/png", "image/jpg", "image/jpeg"],
        "href": "save.html",
        "title": "Save to Drive" 
      }
    ]
  }
}

The intent declaration in the app manifest describes the functionality that your application offers, the data it can work with, and what to launch when the user chooses your application.

In order to use the Drive API, the page has to get an OAuth token for the user. The complete JavaScript implementation of the OAuth flow is available in the project repository.

Once the app receives a valid OAuth token, we can load the Drive client library and perform the file upload request using multipart upload. A Web Intent with action http://webintents.org/save can provide the file data in two ways: as a base64-encoded string or a blob, and our app should support both. With the former, we have to pass the string to the request, while with the latter we have to read the blob content and base64 encode it before we can pass it to insertBase64Data:

const boundary = '-------314159265358979323846';
const delimiter = "\r\n--" + boundary + "\r\n";
const close_delim = "\r\n--" + boundary + "--";

function makeApiCall(authResult) {
  gapi.client.load('drive', 'v2', function() {
    if(window.webkitIntent) {
      var data = window.webkitIntent.data;
      if(data.constructor.name == "Blob") {
        insertFileData(data, authResult, processResponse);
      }
      else if(typeof(data) == "string") {
        var meta = {
          'title': "Test Image " + (new Date()).toJSON(),
          'mimeType': window.webkitIntent.type
        };
        insertBase64Data(data.replace(/data:image\/([^;]*);base64,/,""),
                         window.webkitIntent.type, meta, authResult);
      }
    }
  });
}

function insertBase64Data(data, contentType, metadata, authRequest,
                          callback) {
  var multipartRequestBody =
       delimiter +
       'Content-Type: application/json\r\n\r\n' +
       JSON.stringify(metadata) +
       delimiter +
       'Content-Type: ' + contentType + '\r\n' +
       'Content-Transfer-Encoding: base64\r\n' +
       '\r\n' +
       data +
       close_delim;

  var request = gapi.client.request({
      'path': '/upload/drive/v2/files',
      'method': 'POST',
      'params': {'uploadType': 'multipart'},
      'headers': {
          'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'
      },
      'body': multipartRequestBody});
  if (!callback) {
    callback = function(file) {
      console.log(file.id);
    };
  }
  request.execute(callback);
}

function insertFileData(fileData, authRequest, callback) {
  var reader = new FileReader();
  reader.readAsBinaryString(fileData);
  reader.onload = function(e) {
    var contentType = fileData.type || 'application/octet-stream';
    var metadata = {
      'title': fileData.fileName,
      'mimeType': contentType
    };

    var base64Data = btoa(reader.result);
    insertBase64Data(base64Data, contentType, metadata, 
                     authRequest, callback);
  }
}

Now, any web app that wants to save data to Google Drive could use this service instead of implementing the same functionality by invoking webkitStartActivity with a save intent.

Triggering the Web Intent

An example client app is http://www.imagemator.com. This app lets users manipulate and save images, but has no direct Drive API integration. When the user invokes the “save” intent in imagemator.com, if they have the “Save to Drive” app installed they will see Drive as an option in the list of apps that can fulfill that action:

If the user selects “Save to Drive”, the browser will send a request to the page listed as the href property of the intent declaration -- save.html in the sample manifest above. The following code shows how to trigger the save request:

var fileData = canvas.toDataURL(); 
var intent = new WebkitIntent({'action': 'http://webintents.org/save',
                               'type':'image/png', 'data': fileData});
var onSuccess = function(data) { 
  // handle any data that might be sent back
};
window.navigator.webkitStartActivity(intent, onSuccess);

To learn more about the Web Intents check webintents.org, and visit https://developers.google.com/drive to learn about the Google Drive SDK. The complete sample described in this post is also available on https://github.com/PaulKinlan/WebIntents/tree/master/server/demos/cloudfilepicker.

Claudio Cherubino   profile | twitter | blog

Claudio is an engineer in the Google Drive Developer Relations team. Prior to Google, he worked as software developer, technology evangelist, community manager, consultant, technical translator and has contributed to many open-source projects. His current interests include Google APIs, new technologies and coffee.

Paul Kinlan   profile | twitter | blog

Paul is a Developer Advocate on the Chrome Developer Relations team and one of the engineers on the Web Intents project. His main focus is to help developers make great apps on the web.

3 comments:

anthonydavis said...

If the user selects “Save to Drive”, the browser will send a request to the page listed as the href property of the intent declaration -- save.html in the sample manifest above.

Library Catalog Software

Vinay Singh said...

This comment has been removed by the author.

Vinay Singh said...

Web Intents lets web developers integrate their web apps with third-party services and avoid implementing the same feature with similar services over and over again.Join our community here-
MyLife.com