guacamole實現RDP的下載


1. 配置說明

1.1 主要特別配置以下三項

enable-drive

默認情況下禁用文件傳輸,但啟用文件傳輸后,RDP用戶可以將文件傳輸到持久存在於Guacamole服務器上的虛擬驅動器。通過將此參數設置為“true”來啟用文件傳輸支持。文件將存儲在由“ drive-path”參數指定的目錄中,如果啟用了文件傳輸,則該參數是必需的。

drive-path|Guacamole

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

create-drive-path

如果設置為“true”,並且啟用了文件傳輸,drive-path指定的目錄將自動創建。將只創建路徑中的最終目錄 - 如果路徑之前的其他目錄不存在,則自動創建將失敗,並會記錄錯誤。默認情況下,該drive-path參數指定的目錄 將不會自動創建,並且嘗試將文件傳輸到不存在的目錄將被記錄為錯誤。
如果文件傳輸未啟用,則此參數將被忽略。

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

在瀏覽器登錄到堡壘機,可以看到設備和驅動里面多了一個guacamole RDP驅動

以下是截圖

進入驅動下

理解:

實際上在使用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);
        }
    }
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM