Last active
April 7, 2017 10:17
-
-
Save cowsrule/c2221d76301aed6566d0aba49f9229a6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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