From 2934f4a9be67952d2be03124fc4ff34a3fd9c33b Mon Sep 17 00:00:00 2001 From: Michael Jumper Date: Thu, 2 Jun 2016 15:51:15 -0700 Subject: [PATCH] GUACAMOLE-44: Use Guacamole.FileWriter within ManagedFileUpload (rather than load entire file into memory). --- .../app/client/types/ManagedFileUpload.js | 156 ++++++++---------- 1 file changed, 72 insertions(+), 84 deletions(-) diff --git a/guacamole/src/main/webapp/app/client/types/ManagedFileUpload.js b/guacamole/src/main/webapp/app/client/types/ManagedFileUpload.js index 50d8125ab..761790f5f 100644 --- a/guacamole/src/main/webapp/app/client/types/ManagedFileUpload.js +++ b/guacamole/src/main/webapp/app/client/types/ManagedFileUpload.js @@ -29,16 +29,6 @@ angular.module('client').factory('ManagedFileUpload', ['$rootScope', '$injector' // 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 @@ -136,93 +126,91 @@ angular.module('client').factory('ManagedFileUpload', ['$rootScope', '$injector' ManagedFileUpload.getInstance = function getInstance(client, file, object, streamName) { var managedFileUpload = new ManagedFileUpload(); + var streamAcknowledged = false; - // Construct reader for file - var reader = new FileReader(); - reader.onloadend = function fileContentsLoaded() { + // Open file for writing + var stream; + if (!object) + stream = client.createFileStream(file.type, file.name); - // Open file for writing - var stream; - if (!object) - stream = client.createFileStream(file.type, file.name); + // If object/streamName specified, upload to that instead of a file + // stream + else + stream = object.createOutputStream(file.type, streamName); - // If object/streamName specified, upload to that instead of a file - // stream - else - stream = object.createOutputStream(file.type, streamName); + // Notify that the file transfer is pending + $rootScope.$apply(function uploadStreamOpen() { - var valid = true; - var bytes = new Uint8Array(reader.result); - var offset = 0; + // Init managed upload + managedFileUpload.filename = file.name; + managedFileUpload.mimetype = file.type; + managedFileUpload.progress = 0; + managedFileUpload.length = file.size; - $rootScope.$apply(function uploadStreamOpen() { + // Notify that stream is open + ManagedFileTransferState.setStreamState(managedFileUpload.transferState, + ManagedFileTransferState.StreamState.OPEN); - // 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); + var writer = new Guacamole.FileWriter(stream); - }); - - // Invalidate stream on all errors - // Continue upload when acknowledged - stream.onack = function ackReceived(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); - - // Notify of upload completion - $rootScope.$broadcast('guacUploadComplete', file.name); - - } - - // Otherwise, update progress - else - managedFileUpload.progress = offset; + // Begin upload when stream is acknowledged, notify of any errors + writer.onack = function ackReceived(status) { + // Notify of any errors from the Guacamole server + if (status.isError()) { + $rootScope.$apply(function uploadStreamError() { + ManagedFileTransferState.setStreamState(managedFileUpload.transferState, + ManagedFileTransferState.StreamState.ERROR, + status.code); }); + } - }; // end ack handler + // Begin sending the requested file once stream is acknowledged + else if (!streamAcknowledged) { + writer.sendFile(file); + streamAcknowledged = true; + } + + }; + + // Abort and notify if the file cannot be read + writer.onerror = function fileReadError(file, offset, error) { + + // Abort transfer + writer.sendEnd(); + + // Upload failed + ManagedFileTransferState.setStreamState(managedFileUpload.transferState, + ManagedFileTransferState.StreamState.ERROR); + + }; + + // Notify of upload progress + writer.onprogress = function uploadProgressing(file, length) { + + $rootScope.$apply(function uploadStreamProgress() { + managedFileUpload.progress = length; + }); + + }; + + // Clean up and notify when upload completes + writer.oncomplete = function uploadComplete(file) { + + // If at end, stop upload + writer.sendEnd(); + managedFileUpload.progress = file.size; + + // Upload complete + ManagedFileTransferState.setStreamState(managedFileUpload.transferState, + ManagedFileTransferState.StreamState.CLOSED); + + // Notify of upload completion + $rootScope.$broadcast('guacUploadComplete', file.name); }; - reader.readAsArrayBuffer(file); return managedFileUpload;