How do I add/create/insert files to Google Drive through the API?

81,071

Solution 1

The documentation on insert operations already contains examples in a bunch of programming languages, here is how to do it using the HTTP based protocol of the Google Drive API.

First, POST the new file metadata to the Drive endpoint. It has to be in the form of a File resource JSON object:

POST /drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer <OAuth 2.0 access token here>
...

{
  "title": "file_name.extension",
  "mimeType": "mime/type",
  "description": "Stuff about the file"
}

The response body will be a JSON representation of the newly created File resource. It will look like:

{
  "kind": "drive#file",
  "id": string,
  "etag": etag,
  "selfLink": string,
  "title": "file_name",
  "mimeType": "mime/type",
  "description": "Stuff about the file"
  ...
  "downloadUrl": string,
  ...
}

This is a confirmation that the file entry has been created. Now you need to upload the content. To do that you need to take the ID of the file given by the id JSON attribute in the response above and PUT the content of the actual file to the upload endpoint with an OAuth 2.0 authorized request. It should look like:

PUT /upload/drive/v2/files/{id}?uploadType=media HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer <OAuth 2.0 access token here>
Content-Type: mime/type

<file content here>

You are done :)

There is also a way to do this in 1 single POST request using a multipart request where you post the metadata of the file at the same time as the content. Here is an example:

POST /upload/drive/v2/files HTTP/1.1
Host: www.googleapis.com
Authorization: Bearer <OAuth 2.0 access token here>
Content-Type: multipart/form-data; boundary=287032381131322
...

--287032381131322
Content-Type: application/json

{
  "title": "file_name.extension",
  "mimeType": "mime/type",
  "description": "Stuff about the file"
}
--287032381131322
Content-Type: mime/type

<file content here>
--287032381131322--

The response will contain the metadata of the newly created file. You may also use the Content-Transfer-Encoding: base64 header in the sub-part of the request to be able to pass the data of the file as Base 64 encoded.

Lastly there is also a resumable upload protocol which is convenient to upload large files, offer a pause/resume feature and/or upload files with flaky internet connection.

PS: most of this is now described in Drive's file upload documentation.

Solution 2

Thanks for the explanation! This has taken my hours of going around in circles with the crappy google SDK documentation (sorry I had to get my rant out).

Here's a function I made that will update a text file (as you can see I'm updating html):

  function gd_updateFile(fileId, folderId, text, callback) {

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

    var contentType = "text/html";
    var metadata = {'mimeType': contentType,};

    var multipartRequestBody =
        delimiter +  'Content-Type: application/json\r\n\r\n' +
        JSON.stringify(metadata) +
        delimiter + 'Content-Type: ' + contentType + '\r\n' + '\r\n' +
        text +
        close_delim;

    if (!callback) { callback = function(file) { console.log("Update Complete ",file) }; }

    gapi.client.request({
        'path': '/upload/drive/v2/files/'+folderId+"?fileId="+fileId+"&uploadType=multipart",
        'method': 'PUT',
        'params': {'fileId': fileId, 'uploadType': 'multipart'},
        'headers': {'Content-Type': 'multipart/mixed; boundary="' + boundary + '"'},
        'body': multipartRequestBody,
        callback:callback,
    });
  }

It's a mashup of the google example (which uses a binary file from upload), and the nice explanation from @Nivco above.

Solution 3

4 years later, this is still difficult to figure out. I took @user1158023's answer to support uploading of images. I am using API v3 and superagent.js to help me out (since gapi.client.request is sending the request to content.googleapis.com!?). Hopefully someone may find this useful.

function gd_uploadFile(name, contentType, data, callback) {
    const boundary = '-------314159265358979323846';
    const delimiter = "\r\n--" + boundary + "\r\n";
    const close_delim = "\r\n--" + boundary + "--";

    contentType = contentType || "text/html";
    var metadata = {
        name: name,
        'mimeType': contentType
    };

    var multipartRequestBody =
        delimiter +  'Content-Type: application/json\r\n\r\n' +
        JSON.stringify(metadata) +
        delimiter +
        'Content-Type: ' + contentType + '\r\n';

    //Transfer images as base64 string.
    if (contentType.indexOf('image/') === 0) {
        var pos = data.indexOf('base64,');
        multipartRequestBody += 'Content-Transfer-Encoding: base64\r\n' + '\r\n' +
            data.slice(pos < 0 ? 0 : (pos + 'base64,'.length));
    } else {
        multipartRequestBody +=  + '\r\n' + data;
    }
    multipartRequestBody += close_delim;

    if (!callback) { callback = function(file) { console.log("Update Complete ", file) }; }

    superagent.post('https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart').
        set('Content-Type', 'multipart/form-data;  boundary="' + boundary + '"').
        set('Authorization', 'Bearer ' + gapi.auth.getToken().access_token).
        send(multipartRequestBody).
        end(function () {
            console.log(arguments);
        });
}

//On upload
$('#file')[0].onchange = function () {
    var file = $('#file')[0].files[0];
    if (file && file.type === 'image/jpeg') {
        var reader = new FileReader();
        reader.onloadend = function () {
            var data = reader.result;
            gd_uploadFile('img.jpg', 'image/jpeg', data, function () {
                console.log(arguments);
            });
        }
        reader.readAsDataURL(file);
    }
};

index.html

...
<form>
    <span>Upload: </span><input id="file" type="file" name="myFile">
</form>
...

Solution 4

I whish there were better examples for drive gapis v3... It took me some time to figure out how to upload new content into an existing file created with

gapi.client.drive.files.create({ "name" : "savefile.txt" }).execute(function(file) { console.log("Created file " + file.name + " id: " + file.id); });

but eventually I tried the "lucky" combination of adding the fileId to the path and changing the method to PATCH

function uploadFile(id, text)                                                                                                                                                          
{
  var auth_token = gapi.auth.getToken().access_token;

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

  var metadata = { 
      description : 'savefile for my game',
      'mimeType': 'application/json'
  };  

  var multipartRequestBody =
    delimiter +  'Content-Type: application/json\r\n\r\n' +
    JSON.stringify(metadata) +
    delimiter + 'Content-Type: application/json\r\n\r\n' +
    text +
    close_delim;

  gapi.client.request
    ( { 
     'path': '/upload/drive/v3/files/'+id,
     'method': 'PATCH',
     'params': {'fileId': id, 'uploadType': 'multipart'},
     'headers': { 'Content-Type': 'multipart/form-data; boundary="' + boundary + '"', 'Authorization': 'Bearer ' + auth_token, },
     'body': multipartRequestBody 
     }).execute(function(file) { console.log("Wrote to file " + file.name + " id: " + file.id); }); 
}

But I guess that now the whole documentation at https://developers.google.com/drive/v3/reference/files/update starts to make sense to me :-)

Solution 5

The Google Drive API team released v3 at the end of 2015, and in that release, insert() changed names to create() so as to better reflect the file operation. The documentation has also been improved: there's now a special guide devoted to uploads (simple, multipart, and resumable) that comes with sample code in Java, Python, PHP, C#/.NET, Ruby, JavaScript/Node.js, and iOS/Obj-C for uploading a regular file and another one for importing a CSV file as a Google Sheet.

Just to show up straightforward it is, below is one alternate Python solution (to the sample in the docs) for short files ("simple upload") where you don't need the apiclient.http.MediaFileUpload class. This snippet assumes your auth code works where your service endpoint is DRIVE with a minimum auth scope of https://www.googleapis.com/auth/drive.file.

# filenames & MIMEtypes
DST_FILENAME = 'inventory'
SRC_FILENAME = DST_FILENAME + '.csv'
SHT_MIMETYPE = 'application/vnd.google-apps.spreadsheet'
CSV_MIMETYPE = 'text/csv'

# Import CSV file to Google Drive as a Google Sheets file
METADATA = {'name': DST_FILENAME, 'mimeType': SHT_MIMETYPE}
rsp = DRIVE.files().create(body=METADATA, media_body=SRC_FILENAME).execute()
if rsp:
    print('Imported %r to %r (as %s)' % (SRC_FILENAME, DST_FILENAME, rsp['mimeType']))

Note that if you're writing an Android app, there's a separate Google Drive API for Android with its own set of docs. Finally, if you're using JavaScript on Google Apps Script, both the Drive Service native object and Drive Advanced Service are still using v2 of the API.

Share:
81,071
Niranja
Author by

Niranja

Salesforce Developer at Dreamwares

Updated on July 09, 2022

Comments

  • Niranja
    Niranja almost 2 years

    Need help regarding inserting a file to google drive through api. The api documentation for this purpose does not clearly explains how to send the actual body of the file through http post request.