Skip to content

Instantly share code, notes, and snippets.

@cowsrule
Last active April 7, 2017 10:17
Show Gist options
  • Save cowsrule/c2221d76301aed6566d0aba49f9229a6 to your computer and use it in GitHub Desktop.
Save cowsrule/c2221d76301aed6566d0aba49f9229a6 to your computer and use it in GitHub Desktop.
define(function (require, exports, module)
{
var g = require('globals'),
C = require('Constants'),
goog = require('goog'),
AccountManager = require('AccountManager');
function DriveUploader(file, email, onProgress, onComplete, onError)
{
this.file = file;
this.contentType = file.type;
this.email = email;
this.onComplete = onComplete || g.emptyFn;
this.onError = onError || g.emptyFn;
this.onProgress = onProgress || g.emptyFn;
this._callbacks = [ ];
this.offset = 0;
this.chunkSize = 1 * 1024 * 1024;
this._isDone = false;
this._isCanceled = false;
this._hasError = false;
var params =
{
uploadType: 'resumable'
};
this.url = this._buildURL(undefined, params);
this._percent = 0;
}
DriveUploader.prototype =
{
upload: function ()
{
var token = AccountManager.getDefaultAccessToken();
if (token)
{
var headers =
{
'Authorization': 'Bearer ' + token,
'Content-Type': 'application/json',
'X-Upload-Content-Length': this.file.size,
'X-Upload-Content-Type': this.contentType
};
goog.getAttachFolder(function (attachFolderID)
{
var metadata =
{
title: this.file.name,
mimeType: this.contentType,
parents: attachFolderID ? [ { id: attachFolderID } ] : [ ]
};
var metadataStr;
try
{
metadataStr = JSON.stringify(metadata);
}
catch (err)
{
g.reportError(err);
}
if (metadataStr)
{
g.XHR({
url: this.url,
type: 'POST',
headers: headers,
data: metadataStr,
autoRetry: true,
cb: function (xhr)
{
if (xhr.status < C.HTTPStatusCode.BadRequest)
{
var location = xhr.getResponseHeader('Location');
this.url = location;
this._runUpload();
}
else
{
this._onError(xhr);
}
}.bind(this)
});
}
else
{
this._onError();
}
}.bind(this));
}
},
_runUpload: function ()
{
var endOffset = Math.min(this.offset + this.chunkSize, this.file.size);
var content = this.file.slice(this.offset, endOffset);
var headers =
{
'Content-Type': this.contentType,
'Content-Range': 'bytes ' + this.offset + '-' + (endOffset - 1) + '/' + this.file.size,
'X-Upload-Content-Type': this.contentType
};
g.XHR({
url: this.url,
type: 'PUT',
headers: headers,
data: content,
autoRetry: true,
cb: function (xhr)
{
if (xhr.status === C.HTTPStatusCode.Success || xhr.status === C.HTTPStatusCode.Created)
{
if (!this.isCanceled())
{
this._onComplete(xhr);
}
}
else if (xhr.status === C.HTTPStatusCode.PermRedirect)
{
var newOffset = this._extractRange(xhr.getResponseHeader('Range'));
this.offset = newOffset;
this._onProgress(this.offset / this.file.size);
if (!this.isCanceled())
{
this._runUpload();
}
}
else
{
this._onError(xhr);
}
}.bind(this)
});
},
resume: function ()
{
g.Assert(!this.isDone(), 'Should not resume a completed upload');
this._hasError = false;
this._isCanceled = false;
var headers =
{
'Content-Range': 'bytes */' + this.file.size,
'X-Upload-Content-Type': this.contentType
};
g.XHR({
url: this.url,
type: 'PUT',
headers: headers,
autoRetry: true,
cb: function (xhr)
{
if (xhr.status === C.HTTPStatusCode.PermRedirect)
{
var newOffset = this._extractRange(xhr.getResponseHeader('Range'));
this.offset = newOffset;
this._onProgress(this.offset / this.file.size);
this._runUpload();
}
else
{
this._onError(xhr);
}
}.bind(this)
});
},
cancel: function ()
{
g.Assert(!this._isDone, 'Cannot cancel an upload that is already complete');
this._isCanceled = true;
},
_onError: function (xhr)
{
this._hasError = true;
this.onError();
},
_onComplete: function (xhr)
{
g.Assert(!this._isDone, 'Should not call the complete CB twice');
g.Assert(!this._isCanceled, 'Should not be able to complete a canceled upload');
this._isDone = true;
var metadata = { };
if (xhr && xhr.response)
{
try
{
metadata = JSON.parse(xhr.response);
}
catch (err)
{
g.reportError(err);
}
}
this._onProgress(1);
this.onComplete(metadata);
for (var i = 0; i < this._callbacks.length; ++i)
{
try
{
this._callbacks[i](metadata);
}
catch (err)
{
g.reportError(err);
}
}
this._callbacks = [ ];
},
_onProgress: function (percent)
{
g.Assert(!this._hasError, 'Should not make forward progress when an error has occurred');
this._percent = percent;
this.onProgress(percent);
},
_extractRange: function (range)
{
return g.parseInt(range.match(/\d+/g).pop()) + 1;
},
_buildQueryString: function (params)
{
params = params || {};
return Object.keys(params).map(function (key)
{
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
},
_buildURL: function (id, params)
{
var baseURL = 'https://www.googleapis.com/upload/drive/v2/files/';
var outURL = baseURL;
if (id)
{
outURL += id;
}
var query = this._buildQueryString(params);
if (query)
{
outURL += '?' + query;
}
return outURL;
},
notifyOnComplete: function (fn)
{
g.Assert(this._callbacks.indexOf(fn) < 0, 'Should not include fn twice');
g.Assert(!this.isDone(), 'Should not notify on complete after done');
g.Assert(!this.hasError(), 'Should not notify on complete after error');
this._callbacks.push(fn);
},
isCanceled: function ()
{
return this._isCanceled;
},
isDone: function ()
{
return this._isDone;
},
hasError: function ()
{
return this._hasError;
},
getPercent: function ()
{
return this._percent;
}
};
return DriveUploader;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment