Merge pull request #7056 from nextcloud/oc-28415-enable-chunking-in-authenticated-web-upload

[oc] Enable chunking for bigger files in authenticated web upload
This commit is contained in:
blizzz 2017-11-10 15:35:58 +01:00 committed by GitHub
commit 1a2f9fe678
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 90 additions and 17 deletions

View File

@ -55,3 +55,5 @@ $templateManager->registerTemplate('application/vnd.oasis.opendocument.spreadshe
'name' => $l->t('Recent'), 'name' => $l->t('Recent'),
]; ];
}); });
\OCP\Util::connectHook('\OCP\Config', 'js', '\OCA\Files\App', 'extendJsConfig');

View File

@ -93,7 +93,8 @@
direction: $('#defaultFileSortingDirection').val() direction: $('#defaultFileSortingDirection').val()
}, },
config: this._filesConfig, config: this._filesConfig,
enableUpload: true enableUpload: true,
maxChunkSize: OC.appConfig.files && OC.appConfig.files.max_chunk_size
} }
); );
this.files.initialize(); this.files.initialize();

View File

@ -220,8 +220,8 @@ OC.FileUpload.prototype = {
this.data.headers['If-None-Match'] = '*'; this.data.headers['If-None-Match'] = '*';
} }
var userName = this.uploader.filesClient.getUserName(); var userName = this.uploader.davClient.getUserName();
var password = this.uploader.filesClient.getPassword(); var password = this.uploader.davClient.getPassword();
if (userName) { if (userName) {
// copy username/password from DAV client // copy username/password from DAV client
this.data.headers['Authorization'] = this.data.headers['Authorization'] =
@ -234,7 +234,7 @@ OC.FileUpload.prototype = {
&& this.getFile().size > this.uploader.fileUploadParam.maxChunkSize && this.getFile().size > this.uploader.fileUploadParam.maxChunkSize
) { ) {
data.isChunked = true; data.isChunked = true;
chunkFolderPromise = this.uploader.filesClient.createDirectory( chunkFolderPromise = this.uploader.davClient.createDirectory(
'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId()) 'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId())
); );
// TODO: if fails, it means same id already existed, need to retry // TODO: if fails, it means same id already existed, need to retry
@ -260,9 +260,18 @@ OC.FileUpload.prototype = {
} }
var uid = OC.getCurrentUser().uid; var uid = OC.getCurrentUser().uid;
return this.uploader.filesClient.move( return this.uploader.davClient.move(
'uploads/' + encodeURIComponent(uid) + '/' + encodeURIComponent(this.getId()) + '/.file', 'uploads/' + encodeURIComponent(uid) + '/' + encodeURIComponent(this.getId()) + '/.file',
'files/' + encodeURIComponent(uid) + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()) 'files/' + encodeURIComponent(uid) + '/' + OC.joinPaths(this.getFullPath(), this.getFileName()),
true,
{'X-OC-Mtime': this.getFile().lastModified / 1000}
);
},
_deleteChunkFolder: function() {
// delete transfer directory for this upload
this.uploader.davClient.remove(
'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId())
); );
}, },
@ -271,12 +280,20 @@ OC.FileUpload.prototype = {
*/ */
abort: function() { abort: function() {
if (this.data.isChunked) { if (this.data.isChunked) {
// delete transfer directory for this upload this._deleteChunkFolder();
this.uploader.filesClient.remove(
'uploads/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(this.getId())
);
} }
this.data.abort(); this.data.abort();
this.deleteUpload();
},
/**
* Fail the upload
*/
fail: function() {
this.deleteUpload();
if (this.data.isChunked) {
this._deleteChunkFolder();
}
}, },
/** /**
@ -375,6 +392,13 @@ OC.Uploader.prototype = _.extend({
*/ */
filesClient: null, filesClient: null,
/**
* Webdav client pointing at the root "dav" endpoint
*
* @type OC.Files.Client
*/
davClient: null,
/** /**
* Function that will allow us to know if Ajax uploads are supported * Function that will allow us to know if Ajax uploads are supported
* @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html * @link https://github.com/New-Bamboo/example-ajax-upload/blob/master/public/index.html
@ -721,6 +745,13 @@ OC.Uploader.prototype = _.extend({
this.fileList = options.fileList; this.fileList = options.fileList;
this.filesClient = options.filesClient || OC.Files.getClient(); this.filesClient = options.filesClient || OC.Files.getClient();
this.davClient = new OC.Files.Client({
host: this.filesClient.getHost(),
root: OC.linkToRemoteBase('dav'),
useHTTPS: OC.getProtocol() === 'https',
userName: this.filesClient.getUserName(),
password: this.filesClient.getPassword()
});
$uploadEl = $($uploadEl); $uploadEl = $($uploadEl);
this.$uploadEl = $uploadEl; this.$uploadEl = $uploadEl;
@ -920,7 +951,7 @@ OC.Uploader.prototype = _.extend({
} }
if (upload) { if (upload) {
upload.deleteUpload(); upload.fail();
} }
}, },
/** /**
@ -951,6 +982,10 @@ OC.Uploader.prototype = _.extend({
} }
}; };
if (options.maxChunkSize) {
this.fileUploadParam.maxChunkSize = options.maxChunkSize;
}
// initialize jquery fileupload (blueimp) // initialize jquery fileupload (blueimp)
var fileupload = this.$uploadEl.fileupload(this.fileUploadParam); var fileupload = this.$uploadEl.fileupload(this.fileUploadParam);
@ -1041,7 +1076,6 @@ OC.Uploader.prototype = _.extend({
self.log('progress handle fileuploadstop', e, data); self.log('progress handle fileuploadstop', e, data);
self.clear(); self.clear();
self._hideProgressBar();
self.trigger('stop', e, data); self.trigger('stop', e, data);
}); });
fileupload.on('fileuploadfail', function(e, data) { fileupload.on('fileuploadfail', function(e, data) {
@ -1096,7 +1130,7 @@ OC.Uploader.prototype = _.extend({
// modify the request to adjust it to our own chunking // modify the request to adjust it to our own chunking
var upload = self.getUpload(data); var upload = self.getUpload(data);
var range = data.contentRange.split(' ')[1]; var range = data.contentRange.split(' ')[1];
var chunkId = range.split('/')[0]; var chunkId = range.split('/')[0].split('-')[0];
data.url = OC.getRootPath() + data.url = OC.getRootPath() +
'/remote.php/dav/uploads' + '/remote.php/dav/uploads' +
'/' + encodeURIComponent(OC.getCurrentUser().uid) + '/' + encodeURIComponent(OC.getCurrentUser().uid) +
@ -1108,7 +1142,20 @@ OC.Uploader.prototype = _.extend({
fileupload.on('fileuploaddone', function(e, data) { fileupload.on('fileuploaddone', function(e, data) {
var upload = self.getUpload(data); var upload = self.getUpload(data);
upload.done().then(function() { upload.done().then(function() {
self._hideProgressBar();
self.trigger('done', e, upload); self.trigger('done', e, upload);
}).fail(function(status) {
self._hideProgressBar();
if (status === 507) {
// not enough space
OC.Notification.show(t('files', 'Not enough free space'), {type: 'error'});
self.cancelUploads();
} else if (status === 409) {
OC.Notification.show(t('files', 'Target folder does not exist any more'), {type: 'error'});
} else {
OC.Notification.show(t('files', 'Error when assembling chunks, status code {status}', {status: status}), {type: 'error'});
}
self.trigger('fail', e, data);
}); });
}); });
fileupload.on('fileuploaddrop', function(e, data) { fileupload.on('fileuploaddrop', function(e, data) {

View File

@ -357,7 +357,8 @@
this._uploader = new OC.Uploader($uploadEl, { this._uploader = new OC.Uploader($uploadEl, {
fileList: this, fileList: this,
filesClient: this.filesClient, filesClient: this.filesClient,
dropZone: $('#content') dropZone: $('#content'),
maxChunkSize: options.maxChunkSize
}); });
this.setupUploadEvents(this._uploader); this.setupUploadEvents(this._uploader);

View File

@ -54,4 +54,14 @@ class App {
return self::$navigationManager; return self::$navigationManager;
} }
public static function extendJsConfig($settings) {
$appConfig = json_decode($settings['array']['oc_appconfig'], true);
$maxChunkSize = (int)(\OC::$server->getConfig()->getAppValue('files', 'max_chunk_size', (10 * 1024 * 1024)));
$appConfig['files'] = [
'max_chunk_size' => $maxChunkSize
];
$settings['array']['oc_appconfig'] = json_encode($appConfig);
}
} }

View File

@ -37,6 +37,7 @@
} }
url += options.host + this._root; url += options.host + this._root;
this._host = options.host;
this._defaultHeaders = options.defaultHeaders || { this._defaultHeaders = options.defaultHeaders || {
'X-Requested-With': 'XMLHttpRequest', 'X-Requested-With': 'XMLHttpRequest',
'requesttoken': OC.requestToken 'requesttoken': OC.requestToken
@ -698,10 +699,11 @@
* @param {String} destinationPath destination path * @param {String} destinationPath destination path
* @param {boolean} [allowOverwrite=false] true to allow overwriting, * @param {boolean} [allowOverwrite=false] true to allow overwriting,
* false otherwise * false otherwise
* @param {Object} [headers=null] additional headers
* *
* @return {Promise} promise * @return {Promise} promise
*/ */
move: function(path, destinationPath, allowOverwrite) { move: function(path, destinationPath, allowOverwrite, headers) {
if (!path) { if (!path) {
throw 'Missing argument "path"'; throw 'Missing argument "path"';
} }
@ -712,9 +714,9 @@
var self = this; var self = this;
var deferred = $.Deferred(); var deferred = $.Deferred();
var promise = deferred.promise(); var promise = deferred.promise();
var headers = { headers = _.extend({}, headers, {
'Destination' : this._buildUrl(destinationPath) 'Destination' : this._buildUrl(destinationPath)
}; });
if (!allowOverwrite) { if (!allowOverwrite) {
headers.Overwrite = 'F'; headers.Overwrite = 'F';
@ -828,6 +830,16 @@
*/ */
getBaseUrl: function() { getBaseUrl: function() {
return this._client.baseUrl; return this._client.baseUrl;
},
/**
* Returns the host
*
* @since 13.0.0
* @return {String} base URL
*/
getHost: function() {
return this._host;
} }
}; };