mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-07 05:31:22 +00:00
GUAC-963: Manage file uploads.
This commit is contained in:
@@ -620,100 +620,6 @@ angular.module('client').controller('clientController', ['$scope', '$routeParams
|
||||
});
|
||||
});
|
||||
|
||||
// Mapping of upload stream index to notification object
|
||||
var uploadNotifications = {};
|
||||
|
||||
// Mapping of upload stream index to notification ID
|
||||
var uploadNotificationIDs = {};
|
||||
|
||||
$scope.$on('guacClientFileUploadStart', function handleClientFileUploadStart(event, guacClient, streamIndex, mimetype, filename, length) {
|
||||
$scope.$apply(function() {
|
||||
|
||||
var notification = {
|
||||
className : 'upload',
|
||||
title : 'CLIENT.DIALOG_TITLE_FILE_TRANSFER',
|
||||
text : filename
|
||||
};
|
||||
|
||||
uploadNotifications[streamIndex] = notification;
|
||||
uploadNotificationIDs[streamIndex] = $scope.addNotification(notification);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('guacClientFileUploadProgress', function handleClientFileUploadProgress(event, guacClient, streamIndex, mimetype, filename, length, offset) {
|
||||
$scope.$apply(function() {
|
||||
|
||||
var notification = uploadNotifications[streamIndex];
|
||||
if (notification)
|
||||
notification.progress = getFileProgress('CLIENT.TEXT_FILE_TRANSFER_PROGRESS', offset, length);
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('guacClientFileUploadEnd', function handleClientFileUploadEnd(event, guacClient, streamIndex, mimetype, filename, length) {
|
||||
$scope.$apply(function() {
|
||||
|
||||
var notification = uploadNotifications[streamIndex];
|
||||
var notificationID = uploadNotificationIDs[streamIndex];
|
||||
|
||||
/**
|
||||
* Close the notification.
|
||||
*/
|
||||
var closeNotification = function closeNotification() {
|
||||
$scope.removeNotification(notificationID);
|
||||
delete uploadNotifications[streamIndex];
|
||||
delete uploadNotificationIDs[streamIndex];
|
||||
};
|
||||
|
||||
// Show that the file has uploaded successfully
|
||||
if (notificationID && notification) {
|
||||
delete notification.progress;
|
||||
notification.actions = [
|
||||
{
|
||||
name : 'CLIENT.ACTION_ACKNOWLEDGE',
|
||||
callback : closeNotification
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('guacClientFileUploadError', function handleClientFileUploadError(event, guacClient, streamIndex, mimetype, fileName, length, status) {
|
||||
$scope.$apply(function() {
|
||||
|
||||
var notification = uploadNotifications[streamIndex];
|
||||
var notificationID = uploadNotificationIDs[streamIndex];
|
||||
|
||||
// Determine translation name of error
|
||||
var errorName = (status in UPLOAD_ERRORS) ? status.toString(16).toUpperCase() : "DEFAULT";
|
||||
|
||||
/**
|
||||
* Close the notification.
|
||||
*/
|
||||
var closeNotification = function closeNotification() {
|
||||
$scope.removeNotification(notificationID);
|
||||
delete uploadNotifications[streamIndex];
|
||||
delete uploadNotificationIDs[streamIndex];
|
||||
};
|
||||
|
||||
// Show that the file upload has failed
|
||||
if (notificationID && notification) {
|
||||
delete notification.progress;
|
||||
notification.actions = [
|
||||
{
|
||||
name : 'CLIENT.ACTION_ACKNOWLEDGE',
|
||||
callback : closeNotification
|
||||
}
|
||||
];
|
||||
notification.text = "CLIENT.ERROR_UPLOAD_" + errorName;
|
||||
notification.className = "upload error";
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
// Clean up when view destroyed
|
||||
$scope.$on('$destroy', function clientViewDestroyed() {
|
||||
|
||||
|
@@ -42,8 +42,11 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
||||
templateUrl: 'app/client/templates/guacClient.html',
|
||||
controller: ['$scope', '$injector', '$element', function guacClientController($scope, $injector, $element) {
|
||||
|
||||
// Required types
|
||||
var ManagedClient = $injector.get('ManagedClient');
|
||||
|
||||
// Required services
|
||||
var $window = $injector.get('$window');
|
||||
var $window = $injector.get('$window');
|
||||
|
||||
/**
|
||||
* Whether the local, hardware mouse cursor is in use.
|
||||
@@ -407,26 +410,6 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
||||
client.sendKeyEvent(0, keysym);
|
||||
});
|
||||
|
||||
/**
|
||||
* Converts the given bytes to a base64-encoded string.
|
||||
*
|
||||
* @param {Uint8Array} bytes A Uint8Array which contains the data to be
|
||||
* encoded as base64.
|
||||
* @return {String} The base64-encoded string.
|
||||
*/
|
||||
function getBase64(bytes) {
|
||||
|
||||
var data = "";
|
||||
|
||||
// Produce binary string from bytes in buffer
|
||||
for (var i=0; i<bytes.byteLength; i++)
|
||||
data += String.fromCharCode(bytes[i]);
|
||||
|
||||
// Convert to base64
|
||||
return $window.btoa(data);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Ignores the given event.
|
||||
*
|
||||
@@ -436,68 +419,6 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads the given file to the server.
|
||||
*
|
||||
* @param {File} file The file to upload.
|
||||
*/
|
||||
function uploadFile(file) {
|
||||
|
||||
// Construct reader for file
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function() {
|
||||
|
||||
// Open file for writing
|
||||
var stream = client.createFileStream(file.type, file.name);
|
||||
|
||||
var valid = true;
|
||||
var bytes = new Uint8Array(reader.result);
|
||||
var offset = 0;
|
||||
|
||||
// Add upload notification
|
||||
$scope.$emit('guacClientFileUploadStart', client, stream.index, file.type, file.name, bytes.length);
|
||||
|
||||
// Invalidate stream on all errors
|
||||
// Continue upload when acknowledged
|
||||
stream.onack = function(status) {
|
||||
|
||||
// Handle errors
|
||||
if (status.isError()) {
|
||||
valid = false;
|
||||
$scope.$emit('guacClientFileUploadError', client, stream.index, file.type, file.name, bytes.length, status.code);
|
||||
}
|
||||
|
||||
// Abort upload if stream is invalid
|
||||
if (!valid) return false;
|
||||
|
||||
// Encode packet as base64
|
||||
var slice = bytes.subarray(offset, offset+4096);
|
||||
var base64 = getBase64(slice);
|
||||
|
||||
// Write packet
|
||||
stream.sendBlob(base64);
|
||||
|
||||
// Advance to next packet
|
||||
offset += 4096;
|
||||
|
||||
// If at end, stop upload
|
||||
if (offset >= bytes.length) {
|
||||
stream.sendEnd();
|
||||
$scope.$emit('guacClientFileUploadProgress', client, stream.index, file.type, file.name, bytes.length, bytes.length);
|
||||
$scope.$emit('guacClientFileUploadEnd', client, stream.index, file.type, file.name, bytes.length);
|
||||
}
|
||||
|
||||
// Otherwise, update progress
|
||||
else
|
||||
$scope.$emit('guacClientFileUploadProgress', client, stream.index, file.type, file.name, bytes.length, offset);
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
||||
}
|
||||
|
||||
// Handle and ignore dragenter/dragover
|
||||
displayContainer.addEventListener("dragenter", ignoreEvent, false);
|
||||
@@ -510,12 +431,13 @@ angular.module('client').directive('guacClient', [function guacClient() {
|
||||
e.stopPropagation();
|
||||
|
||||
// Ignore file drops if no attached client
|
||||
if (!client) return;
|
||||
if (!$scope.client)
|
||||
return;
|
||||
|
||||
// Upload each file
|
||||
var files = e.dataTransfer.files;
|
||||
for (var i=0; i<files.length; i++)
|
||||
uploadFile(files[i]);
|
||||
ManagedClient.uploadFile($scope.client, files[i]);
|
||||
|
||||
}, false);
|
||||
|
||||
|
@@ -30,6 +30,7 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
var ClientProperties = $injector.get('ClientProperties');
|
||||
var ManagedClientState = $injector.get('ManagedClientState');
|
||||
var ManagedDisplay = $injector.get('ManagedDisplay');
|
||||
var ManagedFileUpload = $injector.get('ManagedFileUpload');
|
||||
|
||||
// Required services
|
||||
var $window = $injector.get('$window');
|
||||
@@ -100,18 +101,13 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
this.clipboardData = template.clipboardData;
|
||||
|
||||
/**
|
||||
* The current width of the Guacamole display, in pixels.
|
||||
* All uploaded files. As files are uploaded, their progress can be
|
||||
* observed through the elements of this array. It is intended that
|
||||
* this array be manipulated externally as needed.
|
||||
*
|
||||
* @type Number
|
||||
* @type ManagedFileUpload[]
|
||||
*/
|
||||
this.displayWidth = template.displayWidth || 0;
|
||||
|
||||
/**
|
||||
* The current width of the Guacamole display, in pixels.
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
this.displayHeight = template.displayHeight || 0;
|
||||
this.uploads = template.uploads || [];
|
||||
|
||||
/**
|
||||
* The current state of the Guacamole client (idle, connecting,
|
||||
@@ -433,6 +429,21 @@ angular.module('client').factory('ManagedClient', ['$rootScope', '$injector',
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Uploads the given file to the server through the given Guacamole client.
|
||||
* The file transfer can be monitored through the corresponding entry in
|
||||
* the uploads array of the given managedClient.
|
||||
*
|
||||
* @param {ManagedClient} managedClient
|
||||
* The ManagedClient through which the file is to be uploaded.
|
||||
*
|
||||
* @param {File} file
|
||||
* The file to upload.
|
||||
*/
|
||||
ManagedClient.uploadFile = function uploadFile(managedClient, file) {
|
||||
managedClient.uploads.push(ManagedFileUpload.getInstance(managedClient.client, file));
|
||||
};
|
||||
|
||||
return ManagedClient;
|
||||
|
||||
}]);
|
@@ -0,0 +1,136 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the ManagedFileTransferState class used by the guacClientManager
|
||||
* service.
|
||||
*/
|
||||
angular.module('client').factory('ManagedFileTransferState', [function defineManagedFileTransferState() {
|
||||
|
||||
/**
|
||||
* Object which represents the state of a Guacamole stream, including any
|
||||
* error conditions.
|
||||
*
|
||||
* @constructor
|
||||
* @param {ManagedFileTransferState|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* ManagedFileTransferState.
|
||||
*/
|
||||
var ManagedFileTransferState = function ManagedFileTransferState(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The current stream state. Valid values are described by
|
||||
* ManagedFileTransferState.StreamState.
|
||||
*
|
||||
* @type String
|
||||
* @default ManagedFileTransferState.StreamState.IDLE
|
||||
*/
|
||||
this.streamState = template.streamState || ManagedFileTransferState.StreamState.IDLE;
|
||||
|
||||
/**
|
||||
* The status code of the current error condition, if streamState
|
||||
* is ERROR. For all other streamState values, this will be
|
||||
* @link{Guacamole.Status.Code.SUCCESS}.
|
||||
*
|
||||
* @type Number
|
||||
* @default Guacamole.Status.Code.SUCCESS
|
||||
*/
|
||||
this.statusCode = template.statusCode || Guacamole.Status.Code.SUCCESS;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Valid stream state strings. Each state string is associated with a
|
||||
* specific state of a Guacamole stream.
|
||||
*/
|
||||
ManagedFileTransferState.StreamState = {
|
||||
|
||||
/**
|
||||
* The stream has not yet been opened.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
IDLE : "IDLE",
|
||||
|
||||
/**
|
||||
* The stream has been successfully established. Data can be sent or
|
||||
* received.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
OPEN : "OPEN",
|
||||
|
||||
/**
|
||||
* The stream has terminated successfully. No errors are indicated.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
CLOSED : "CLOSED",
|
||||
|
||||
/**
|
||||
* The stream has terminated due to an error. The associated error code
|
||||
* is stored in statusCode.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
ERROR : "ERROR"
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets the current transfer state and, if given, the associated status
|
||||
* code. If an error is already represented, this function has no effect.
|
||||
*
|
||||
* @param {ManagedFileTransferState} transferState
|
||||
* The ManagedFileTransferState to update.
|
||||
*
|
||||
* @param {String} streamState
|
||||
* The stream state to assign to the given ManagedFileTransferState, as
|
||||
* listed within ManagedFileTransferState.StreamState.
|
||||
*
|
||||
* @param {Number} [statusCode]
|
||||
* The status code to assign to the given ManagedFileTransferState, if
|
||||
* any, as listed within Guacamole.Status.Code. If no status code is
|
||||
* specified, the status code of the ManagedFileTransferState is not
|
||||
* touched.
|
||||
*/
|
||||
ManagedFileTransferState.setStreamState = function(transferState, streamState, statusCode) {
|
||||
|
||||
// Do not set state after an error is registered
|
||||
if (transferState.streamState === ManagedFileTransferState.StreamState.ERROR)
|
||||
return;
|
||||
|
||||
// Update stream state
|
||||
transferState.streamState = streamState;
|
||||
|
||||
// Set status code, if given
|
||||
if (statusCode)
|
||||
transferState.statusCode = statusCode;
|
||||
|
||||
};
|
||||
|
||||
return ManagedFileTransferState;
|
||||
|
||||
}]);
|
218
guacamole/src/main/webapp/app/client/types/ManagedFileUpload.js
Normal file
218
guacamole/src/main/webapp/app/client/types/ManagedFileUpload.js
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
* Copyright (C) 2014 Glyptodon LLC
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the ManagedFileUpload class used by the guacClientManager service.
|
||||
*/
|
||||
angular.module('client').factory('ManagedFileUpload', ['$rootScope', '$injector',
|
||||
function defineManagedFileUpload($rootScope, $injector) {
|
||||
|
||||
// Required types
|
||||
var ManagedFileTransferState = $injector.get('ManagedFileTransferState');
|
||||
|
||||
// Required services
|
||||
var $window = $injector.get('$window');
|
||||
|
||||
/**
|
||||
* The maximum number of bytes to include in each blob for the Guacamole
|
||||
* file stream. Note that this, along with instruction opcode and protocol-
|
||||
* related overhead, must not exceed the 8192 byte maximum imposed by the
|
||||
* Guacamole protocol.
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
var STREAM_BLOB_SIZE = 4096;
|
||||
|
||||
/**
|
||||
* Object which serves as a surrogate interface, encapsulating a Guacamole
|
||||
* file upload while it is active, allowing it to be detached and
|
||||
* reattached from different client views.
|
||||
*
|
||||
* @constructor
|
||||
* @param {ManagedFileUpload|Object} [template={}]
|
||||
* The object whose properties should be copied within the new
|
||||
* ManagedFileUpload.
|
||||
*/
|
||||
var ManagedFileUpload = function ManagedFileUpload(template) {
|
||||
|
||||
// Use empty object by default
|
||||
template = template || {};
|
||||
|
||||
/**
|
||||
* The current state of the file transfer stream.
|
||||
*
|
||||
* @type ManagedFileTransferState
|
||||
*/
|
||||
this.transferState = template.transferState || new ManagedFileTransferState();
|
||||
|
||||
/**
|
||||
* The mimetype of the file being transferred.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.mimetype = template.mimetype;
|
||||
|
||||
/**
|
||||
* The filename of the file being transferred.
|
||||
*
|
||||
* @type String
|
||||
*/
|
||||
this.filename = template.filename;
|
||||
|
||||
/**
|
||||
* The number of bytes transferred so far.
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
this.progress = template.progress;
|
||||
|
||||
/**
|
||||
* The total number of bytes in the file.
|
||||
*
|
||||
* @type Number
|
||||
*/
|
||||
this.length = template.length;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the given bytes to a base64-encoded string.
|
||||
*
|
||||
* @param {Uint8Array} bytes A Uint8Array which contains the data to be
|
||||
* encoded as base64.
|
||||
* @return {String} The base64-encoded string.
|
||||
*/
|
||||
var getBase64 = function getBase64(bytes) {
|
||||
|
||||
var data = "";
|
||||
|
||||
// Produce binary string from bytes in buffer
|
||||
for (var i=0; i<bytes.byteLength; i++)
|
||||
data += String.fromCharCode(bytes[i]);
|
||||
|
||||
// Convert to base64
|
||||
return $window.btoa(data);
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new ManagedFileUpload which uploads the given file to the
|
||||
* server through the given Guacamole client.
|
||||
*
|
||||
* @param {Guacamole.Client} client
|
||||
* The Guacamole client through which the file is to be uploaded.
|
||||
*
|
||||
* @param {File} file
|
||||
* The file to upload.
|
||||
*
|
||||
* @return {ManagedFileUpload}
|
||||
* A new ManagedFileUpload object which can be used to track the
|
||||
* progress of the upload.
|
||||
*/
|
||||
ManagedFileUpload.getInstance = function getInstance(client, file) {
|
||||
|
||||
var managedFileUpload = new ManagedFileUpload();
|
||||
|
||||
// Construct reader for file
|
||||
var reader = new FileReader();
|
||||
reader.onloadend = function() {
|
||||
|
||||
// Open file for writing
|
||||
var stream = client.createFileStream(file.type, file.name);
|
||||
|
||||
var valid = true;
|
||||
var bytes = new Uint8Array(reader.result);
|
||||
var offset = 0;
|
||||
|
||||
$rootScope.$apply(function uploadStreamOpen() {
|
||||
|
||||
// Init managed upload
|
||||
managedFileUpload.filename = file.name;
|
||||
managedFileUpload.mimetype = file.type;
|
||||
managedFileUpload.progress = 0;
|
||||
managedFileUpload.length = bytes.length;
|
||||
|
||||
// Notify that stream is open
|
||||
ManagedFileTransferState.setStreamState(managedFileUpload.transferState,
|
||||
ManagedFileTransferState.StreamState.OPEN);
|
||||
|
||||
});
|
||||
|
||||
// Invalidate stream on all errors
|
||||
// Continue upload when acknowledged
|
||||
stream.onack = function(status) {
|
||||
|
||||
// Handle errors
|
||||
if (status.isError()) {
|
||||
valid = false;
|
||||
$rootScope.$apply(function uploadStreamError() {
|
||||
ManagedFileTransferState.setStreamState(managedFileUpload.transferState,
|
||||
ManagedFileTransferState.StreamState.ERROR,
|
||||
status.code);
|
||||
});
|
||||
}
|
||||
|
||||
// Abort upload if stream is invalid
|
||||
if (!valid)
|
||||
return false;
|
||||
|
||||
// Encode packet as base64
|
||||
var slice = bytes.subarray(offset, offset + STREAM_BLOB_SIZE);
|
||||
var base64 = getBase64(slice);
|
||||
|
||||
// Write packet
|
||||
stream.sendBlob(base64);
|
||||
|
||||
// Advance to next packet
|
||||
offset += STREAM_BLOB_SIZE;
|
||||
|
||||
$rootScope.$apply(function uploadStreamProgress() {
|
||||
|
||||
// If at end, stop upload
|
||||
if (offset >= bytes.length) {
|
||||
stream.sendEnd();
|
||||
managedFileUpload.progress = bytes.length;
|
||||
|
||||
// Upload complete
|
||||
ManagedFileTransferState.setStreamState(managedFileUpload.transferState,
|
||||
ManagedFileTransferState.StreamState.CLOSED);
|
||||
|
||||
}
|
||||
|
||||
// Otherwise, update progress
|
||||
else
|
||||
managedFileUpload.progress = offset;
|
||||
|
||||
});
|
||||
|
||||
}; // end ack handler
|
||||
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
|
||||
return managedFileUpload;
|
||||
|
||||
};
|
||||
|
||||
return ManagedFileUpload;
|
||||
|
||||
}]);
|
Reference in New Issue
Block a user