mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-06 05:07:41 +00:00
GUACAMOLE-1320: Merge support for chunked file uploads.
This commit is contained in:
@@ -163,12 +163,14 @@ angular.module('client').factory('ManagedFileUpload', ['$rootScope', '$injector'
|
|||||||
|
|
||||||
// Upload complete
|
// Upload complete
|
||||||
managedFileUpload.progress = file.size;
|
managedFileUpload.progress = file.size;
|
||||||
|
|
||||||
|
// Close the stream
|
||||||
|
stream.sendEnd();
|
||||||
ManagedFileTransferState.setStreamState(managedFileUpload.transferState,
|
ManagedFileTransferState.setStreamState(managedFileUpload.transferState,
|
||||||
ManagedFileTransferState.StreamState.CLOSED);
|
ManagedFileTransferState.StreamState.CLOSED);
|
||||||
|
|
||||||
// Notify of upload completion
|
// Notify of upload completion
|
||||||
$rootScope.$broadcast('guacUploadComplete', file.name);
|
$rootScope.$broadcast('guacUploadComplete', file.name);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Notify if upload fails
|
// Notify if upload fails
|
||||||
@@ -186,11 +188,15 @@ angular.module('client').factory('ManagedFileUpload', ['$rootScope', '$injector'
|
|||||||
ManagedFileTransferState.StreamState.ERROR,
|
ManagedFileTransferState.StreamState.ERROR,
|
||||||
Guacamole.Status.Code.INTERNAL_ERROR);
|
Guacamole.Status.Code.INTERNAL_ERROR);
|
||||||
|
|
||||||
|
// Close the stream
|
||||||
|
stream.sendEnd();
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Ignore all further acks
|
// Ignore all further acks
|
||||||
stream.onack = null;
|
stream.onack = null;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return managedFileUpload;
|
return managedFileUpload;
|
||||||
|
@@ -54,6 +54,15 @@ angular.module('rest').factory('tunnelService', ['$injector',
|
|||||||
*/
|
*/
|
||||||
var DOWNLOAD_CLEANUP_WAIT = 5000;
|
var DOWNLOAD_CLEANUP_WAIT = 5000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The maximum size a chunk may be during uploadToStream() in bytes.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @constant
|
||||||
|
* @type Number
|
||||||
|
*/
|
||||||
|
const CHUNK_SIZE = 1024 * 1024 * 4;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes a request to the REST API to get the list of all tunnels
|
* Makes a request to the REST API to get the list of all tunnels
|
||||||
* associated with in-progress connections, returning a promise that
|
* associated with in-progress connections, returning a promise that
|
||||||
@@ -301,25 +310,60 @@ angular.module('rest').factory('tunnelService', ['$injector',
|
|||||||
+ '/' + encodeURIComponent(sanitizeFilename(file.name))
|
+ '/' + encodeURIComponent(sanitizeFilename(file.name))
|
||||||
+ '?token=' + encodeURIComponent(authenticationService.getCurrentToken());
|
+ '?token=' + encodeURIComponent(authenticationService.getCurrentToken());
|
||||||
|
|
||||||
var xhr = new XMLHttpRequest();
|
/**
|
||||||
|
* Creates a chunk of the inputted file to be uploaded.
|
||||||
|
*
|
||||||
|
* @param {Number} offset
|
||||||
|
* The byte at which to begin the chunk.
|
||||||
|
*
|
||||||
|
* @return {File}
|
||||||
|
* The file chunk created by this function.
|
||||||
|
*/
|
||||||
|
const createChunk = (offset) => {
|
||||||
|
var chunkEnd = Math.min(offset + CHUNK_SIZE, file.size);
|
||||||
|
const chunk = file.slice(offset, chunkEnd);
|
||||||
|
return chunk;
|
||||||
|
};
|
||||||
|
|
||||||
// Invoke provided callback if upload tracking is supported
|
/**
|
||||||
|
* POSTs the inputted chunks and recursively calls uploadHandler()
|
||||||
|
* until the upload is complete.
|
||||||
|
*
|
||||||
|
* @param {File} chunk
|
||||||
|
* The chunk to be uploaded to the stream.
|
||||||
|
*
|
||||||
|
* @param {Number} offset
|
||||||
|
* The byte at which the inputted chunk begins.
|
||||||
|
*/
|
||||||
|
const uploadChunk = (chunk, offset) => {
|
||||||
|
var xhr = new XMLHttpRequest();
|
||||||
|
xhr.open('POST', url, true);
|
||||||
|
|
||||||
|
// Invoke provided callback if upload tracking is supported.
|
||||||
if (progressCallback && xhr.upload) {
|
if (progressCallback && xhr.upload) {
|
||||||
xhr.upload.addEventListener('progress', function updateProgress(e) {
|
xhr.upload.addEventListener('progress', function updateProgress(e) {
|
||||||
progressCallback(e.loaded);
|
progressCallback(e.loaded + offset);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
// Resolve/reject promise once upload has stopped
|
// Continue to next chunk, resolve, or reject promise as appropriate
|
||||||
|
// once upload has stopped
|
||||||
xhr.onreadystatechange = function uploadStatusChanged() {
|
xhr.onreadystatechange = function uploadStatusChanged() {
|
||||||
|
|
||||||
// Ignore state changes prior to completion
|
// Ignore state changes prior to completion.
|
||||||
if (xhr.readyState !== 4)
|
if (xhr.readyState !== 4)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Resolve if HTTP status code indicates success
|
// Resolve if last chunk or begin next chunk if HTTP status
|
||||||
if (xhr.status >= 200 && xhr.status < 300)
|
// code indicates success.
|
||||||
|
if (xhr.status >= 200 && xhr.status < 300) {
|
||||||
|
offset += CHUNK_SIZE;
|
||||||
|
|
||||||
|
if (offset < file.size)
|
||||||
|
uploadHandler(offset);
|
||||||
|
else
|
||||||
deferred.resolve();
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
// Parse and reject with resulting JSON error
|
// Parse and reject with resulting JSON error
|
||||||
else if (xhr.getResponseHeader('Content-Type') === 'application/json')
|
else if (xhr.getResponseHeader('Content-Type') === 'application/json')
|
||||||
@@ -344,8 +388,25 @@ angular.module('rest').factory('tunnelService', ['$injector',
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Perform upload
|
// Perform upload
|
||||||
xhr.open('POST', url, true);
|
xhr.send(chunk);
|
||||||
xhr.send(file);
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the recursive upload process. Each time it is called, a
|
||||||
|
* chunk is made with createChunk(), starting at the offset parameter.
|
||||||
|
* The chunk is then sent by uploadChunk(), which recursively calls
|
||||||
|
* this handler until the upload process is either completed and the
|
||||||
|
* promise is resolved, or fails and the promise is rejected.
|
||||||
|
*
|
||||||
|
* @param {Number} offset
|
||||||
|
* The byte at which to begin the chunk.
|
||||||
|
*/
|
||||||
|
const uploadHandler = (offset) => {
|
||||||
|
uploadChunk(createChunk(offset), offset);
|
||||||
|
};
|
||||||
|
|
||||||
|
uploadHandler(0);
|
||||||
|
|
||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
|
|
||||||
|
@@ -94,8 +94,7 @@ public class InputStreamInterceptingFilter
|
|||||||
/**
|
/**
|
||||||
* Reads the next chunk of data from the InputStream associated with an
|
* Reads the next chunk of data from the InputStream associated with an
|
||||||
* intercepted stream, sending that data as a "blob" instruction over the
|
* intercepted stream, sending that data as a "blob" instruction over the
|
||||||
* GuacamoleTunnel associated with this filter. If the end of the
|
* GuacamoleTunnel associated with this filter.
|
||||||
* InputStream is reached, an "end" instruction will automatically be sent.
|
|
||||||
*
|
*
|
||||||
* @param stream
|
* @param stream
|
||||||
* The stream from which the next chunk of data should be read.
|
* The stream from which the next chunk of data should be read.
|
||||||
@@ -112,9 +111,8 @@ public class InputStreamInterceptingFilter
|
|||||||
// End stream if no more data
|
// End stream if no more data
|
||||||
if (length == -1) {
|
if (length == -1) {
|
||||||
|
|
||||||
// Close stream, send end if the stream is still valid
|
// Close stream
|
||||||
if (closeInterceptedStream(stream))
|
closeInterceptedStream(stream);
|
||||||
sendEnd(stream.getIndex());
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user