• 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);
            }
        }
    }
    
  • 相关阅读:
    Dapper and Repository Pattern in MVC
    在linux中使用多个redis端口来构建redis集群
    搭建Sql Server AlwaysOn 视频教程
    支付宝支付插件使用文档
    NopCommerce添加事务机制
    NopCommerce(3.9)作业调度插件
    微信扫码支付插件使用文档
    生成HTML表格的后台模板代码
    json字符串和字典类型的相互转换
    NopCommerce适应多数据库方案
  • 原文地址:https://www.cnblogs.com/redirect/p/10066735.html
Copyright © 2020-2023  润新知