We have used node with express and Google Drive API as storage system (server-to-server exchange using the oAuth2 / JWT). It is not clear why this was working for a few months but is now producing errors. Working examples or concrete advice would be much appreciated as we are hoping to keep current solution rather than having to change to amazon.
Receiving 3 error messages at various times.
1. At times receiving a 401 - indicating that the access token is not refreshed (does JWT not handle this automatically?)
2. Body attribute missing in multipart error when uploading a file
3. Most critically, unable to display the uploaded file using webContentLink as google drive responds exceeded daily limits. This does not make any sense as the error occurs when viewed even straight after first uploaded.
Precursors
- Drive API is set to ON in google console
- Drive API quota shows we are well within our usage limit
- Using googleapis (npm install googleapis)
- Have downloaded a key file and generated a .pem file used when instantiating the JWT
Assumption
- JWT automatically refreshes
- When uploaded file to a shared folder (anyone on the web), webContentLink can be used in an tag and is visible.
Following is a simplified structure of what we are doing:
// Express controller: /Controller/Google/UploadSample.js
// Description: Basic use of Google Drive API to upload incoming file
var googleapis = require('googleapis'); // npm install googleapis
var parentFolderId = '<id of a publicly shared folder>'; // Folder already created
// Create a JWT used for server communication
var oauth2Client = new googleapis.auth.JWT( '<service_user>@developer.gserviceaccount.com',
'key.pem',
null,
["https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/drive.appdata",
"https://www.googleapis.com/auth/drive.apps.readonly",
"https://www.googleapis.com/auth/drive.file",
"https://www.googleapis.com/auth/drive.metadata.readonly",
"https://www.googleapis.com/auth/drive.readonly"]);
// Authorize to obtain token, we are assuming this refreshes automatically
oauth2Client.authorize(function(err, result) {
console.log("Google Drive - Authentication");
if (!err) {
// OK: Everything is fine
oauth2Client.credentials = result;
console.log("Authentication Success");
} else {
// Error: Check API or credential info
console.log("Authentication Failed");
console.log(err)
}
});
// Recieves a post request with a single file and uploads this to Google Drive
exports.uploadPost = function( req, res, next )
{
// Locate the file in the post
var file = req.files.file;
googleapis.discover('drive', 'v2').execute(function(err, client) {
if (err)
{
// Error: Unable to use drive client
console.log(err);
res.json(err);
} else {
// OK: Drive client available - using insert and passing the file into a (pre-created) shared folder
client.drive.files
.insert({ title: file.name, mimeType: file.type, parents: [{'kind': 'drive#fileLink', 'id': parentFolderId}] })
.withMedia( file.type, imagedata )
.withAuthClient(oauth2Client).execute(function( e, data, callback)
{
if (e)
{ // Error: E.g. '[Error: Body attribute missing in multipart.]' or 'invalid_grant'
console.log(e)
res.json(e);
} else {
// OK: Data recieved - upload worked: returns a JSON with accessible links
res.json(data); // JSON returned pass it on
// Inspect the element webContentLink & thumbnailLink
console.log(data.webContentLink); // Link to content - will download the file when pasted into the browser
console.log(data.thumbnailLink); // Link to thumbnail - will download a downsized (w220) thumbnail photo
}
});
}
});
};