guacamole實現RDP的下載

1. 配置說明

1.1 主要特別配置如下三項

enable-drivegit

默認狀況下禁用文件傳輸,但啓用文件傳輸後,RDP用戶能夠將文件傳輸到持久存在於Guacamole服務器上的虛擬驅動器。經過將此參數設置爲「true」來啓用文件傳輸支持。文件將存儲在由「 drive-path」參數指定的目錄中,若是啓用了文件傳輸,則該參數是必需的。github

drive-path|Guacamolejson

服務器上應存儲傳輸文件的目錄。該目錄必須對guacd可訪問,而且對運行guacd的用戶可讀寫。此參數不指RDP服務器上的目錄。若是文件傳輸未啓用,則此參數將被忽略。瀏覽器

create-drive-path服務器

若是設置爲「true」,而且啓用了文件傳輸,drive-path指定的目錄將自動建立。將只建立路徑中的最終目錄 - 若是路徑以前的其餘目錄不存在,則自動建立將失敗,並會記錄錯誤。默認狀況下,該drive-path參數指定的目錄 將不會自動建立,而且嘗試將文件傳輸到不存在的目錄將被記錄爲錯誤。
若是文件傳輸未啓用,則此參數將被忽略。app

  • 參考配置
enable-drive = true
drive-path = '/yourpath'
create-drive-path = true

在瀏覽器登陸到堡壘機,能夠看到設備和驅動裏面多了一個guacamole RDP驅動源碼分析

如下是截圖ui

進入驅動下this

理解:3d

實際上在使用RDP協議的時候,有下面的關係。

瀏覽器<->guacamole服務器<->真實服務器

咱們看到的guacamole RDP,指向的是guacamole服務器的 /yourpath文件夾,你把文件拖動到這個驅動下,就把文件上傳到guacamole服務器的 /yourpath目錄下了,接下來咱們能夠經過瀏覽器下載guacamole服務器的文件,這個須要本身去實現。特別注意,發現有個download文件夾,默認存在而且不能刪除掉,做用就是你在堡壘機上操做把文件拖進去,會向瀏覽器發送file命令(以下圖所示),而後咱們能夠在cilent端進行監聽,而後實現拖動自動下載。

2. 源碼分析實現

2.1 把指令轉換成相應的client操做

/**
 * Handlers for all instruction opcodes receivable by a Guacamole protocol
 * client.
 * @private
 */
var instructionHandlers = {
    ...其它指令
    
    "file": function(parameters) {
    
        //處理參數
        var stream_index = parseInt(parameters[0]);
        var mimetype = parameters[1];
        var filename = parameters[2];

        // Create stream 
        if (guac_client.onfile) {
            //這裏根據index獲得了輸入流的抽象,注意下InputStream方法
            var stream = streams[stream_index] = new Guacamole.InputStream(guac_client, stream_index);
            //建立完流以後,調用了client的onfile方法,而且把參數傳遞過去,咱們須要在client的onfile方法裏面處理輸入的流。
            guac_client.onfile(stream, mimetype, filename);
        }

        // Otherwise, unsupported
        else
            guac_client.sendAck(stream_index, "File transfer unsupported", 0x0100);

    },
        
    ...其它指令
}

如下是InputStream的代碼,其實是進行了一些屬性的初始化工做

/**
 * An input stream abstraction used by the Guacamole client to facilitate
 * transfer of files or other binary data.
 * 
 * @constructor
 * @param {Guacamole.Client} client The client owning this stream.
 * @param {Number} index The index of this stream.
 */
Guacamole.InputStream = function(client, index) {

    /**
     * Reference to this stream.
     * @private
     */
    var guac_stream = this;

    /**
     * The index of this stream.
     * @type {Number}
     */
    this.index = index;

    /**
     * Called when a blob of data is received.
     * 
     * @event
     * @param {String} data The received base64 data.
     */
    this.onblob = null;

    /**
     * Called when this stream is closed.
     * 
     * @event
     */
    this.onend = null;

    /**
     * Acknowledges the receipt of a blob.
     * 
     * @param {String} message A human-readable message describing the error
     *                         or status.
     * @param {Number} code The error code, if any, or 0 for success.
     */
    this.sendAck = function(message, code) {
        client.sendAck(guac_stream.index, message, code);
    };

};

2.2 client的onfile方法

這個本身實現,做用就是監聽上面的onfile事件,並進一步處理

client.onfile = function(stream, mimetype, filename){
        //通知服務端,已經收到了stream
        stream.sendAck('OK', Guacamole.Status.Code.SUCCESS);
        //開始處理輸入流,這裏封裝了一個downloadFile方法
        downloadFile(stream, mimetype, filename);
    }

2.3 處理輸入流的邏輯

downloadFile = (stream, mimetype, filename) => {
    //拿到的流不能直接使用,先實例化一個處理器,使用blob reader處理數據
    var blob_builder;
    if      (window.BlobBuilder)       blob_builder = new BlobBuilder();
    else if (window.WebKitBlobBuilder) blob_builder = new WebKitBlobBuilder();
    else if (window.MozBlobBuilder)    blob_builder = new MozBlobBuilder();
    else
        blob_builder = new (function() {

            var blobs = [];

            /** @ignore */
            this.append = function(data) {
                blobs.push(new Blob([data], {"type": mimetype}));
            };

            /** @ignore */
            this.getBlob = function() {
                return new Blob(blobs, {"type": mimetype});
            };

        })();

    // Append received blobs
    stream.onblob = function(data) {

        // Convert to ArrayBuffer
        var binary = window.atob(data);
        var arrayBuffer = new ArrayBuffer(binary.length);
        var bufferView = new Uint8Array(arrayBuffer);

        for (var i=0; i<binary.length; i++)
            bufferView[i] = binary.charCodeAt(i);
            
        //收到後就交給blob_builder
        blob_builder.append(arrayBuffer);
        length += arrayBuffer.byteLength;

        // Send success response
        stream.sendAck("OK", 0x0000);

    };

    stream.onend = function(){
        //結束的時候,獲取blob_builder裏面的可用數據
        var blob_data = blob_builder.getBlob();

        //數據傳輸完成後進行下載等處理
        if(mimetype.indexOf('stream-index+json') != -1){
            //若是是文件夾,使用filereader讀取blob數據,能夠得到該文件夾下的文件和目錄的名稱和類型,是一個json形式
            var blob_reader = new FileReader();
            blob_reader.addEventListener("loadend", function() {
                let folder_content = JSON.parse(blob_reader.result)
                //從新組織當前文件目錄,appendFileItem是本身封裝的文件系統動態展現
                appendFileItem(folder_content)
                $("#header_title").text(filename);
            });
            blob_reader.readAsBinaryString(blob_data);
        } else {
            //若是是文件,直接下載,可是須要解決個問題,就是如何下載blob數據
            //借鑑了https://github.com/eligrey/FileSaver.js這個庫
            var file_arr = filename.split("/");
            var download_file_name = file_arr[file_arr.length - 1];
            saveAs(blob_data, download_file_name);
        }
    }
}
相關文章
相關標籤/搜索