aardio web服务器程序模版代码 补充注释





import fonts.fontAwesome;
import win.ui;
/*DSG{{*/
var winform = win.form(text="asynHttpServer - 异步HTTP服务器";right=807;bottom=465;bgcolor=16777215;border="none";max=false)
winform.add(
bk={cls="bk";left=-2;top=-5;right=810;bottom=29;bgcolor=12639424;z=9};
bkplus={cls="bkplus";text="asynHttpServer - 扫码传文件";left=18;top=4;right=203;bottom=26;align="left";color=5921370;z=10};
btnOpen={cls="plus";text='\uF115';left=444;top=50;right=479;bottom=75;dr=1;dt=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=5};
btnOpenUpload={cls="plus";text="打开上传目录";left=568;top=429;right=709;bottom=458;dr=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome');padding={left=8;top=2}};iconText='\uF115';notify=1;textPadding={left=20};z=11};
btnStart={cls="plus";text="重启服务";left=655;top=47;right=755;bottom=76;bgcolor=14935259;dr=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome');padding={left=8;top=2}};iconText='\uF233';notify=1;textPadding={left=20};z=4};
editDocumentRoot={cls="plus";left=131;top=49;right=430;bottom=73;align="right";border={bottom=1;color=-8355712};dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-16);z=7};
editPassword={cls="plus";left=441;top=84;right=632;bottom=108;align="left";border={bottom=1;color=-8355712};dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-16);z=13};
editPort={cls="plus";left=550;top=49;right=628;bottom=73;align="left";border={bottom=1;color=-8355712};dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-16);z=8};
plus={cls="plus";text="访问密码:";left=332;top=90;right=433;bottom=114;align="right";dr=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=12};
qr={cls="plus";left=499;top=132;right=760;bottom=418;db=1;dr=1;dt=1;repeat="scale";z=6};
static={cls="plus";text="端口:";left=484;top=52;right=547;bottom=76;align="right";dr=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=2};
static2={cls="plus";text="网站根目录:";left=15;top=52;right=129;bottom=76;align="right";dl=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=3};
txtMessage={cls="richedit";left=42;top=132;right=469;bottom=418;autohscroll=false;db=1;dl=1;dr=1;dt=1;link=1;multiline=1;vscroll=1;z=1}
)
/*}}*/

winform.btnStart.skin( {
    background={
        default=0x668FB2B0;
        hover=0xFF928BB3;
        disabled=0xFFCCCCCC; 
    }
})

winform.btnOpen.skin( {
    background={
        default=0;
        hover=0xFF928BB3;
        disabled=0xFFCCCCCC; 
    }
})

winform.btnOpenUpload.skin( {
    background={
        default=0;
        hover=0xFF928BB3;
        disabled=0xFFCCCCCC; 
    }
})

//从配置文件中读取文本信息标题,如果有写入文本信息框,如果没有获取系统“我的文档”文件夹路径并写入文本信息框
//在这里,作者没有定义一个专用的变量,而是直接使用文本信息框的属性进行临时的文本存储,这种方式在后面反复出现
//这种方式可能是为了节省资源,但造成可读性差。并且,只有在顺序执行的过程中才可以用。
import fsys.config;
config = fsys.config("/config/"); 
if( io.exist(config.winform.txtMessage) ){
    winform.txtMessage.text = config.winform.txtMessage;
}
else {
    //获取系统“我的文档”路径,通过ide智能提示,还可以获得诸如系统文件夹、桌面、appdata等特殊文件夹的路径
    winform.txtMessage.text = io.getSpecial(0x5/*_CSIDL_MYDOCUMENTS*/)
    
}

//请注意此处引入的直接是WebSocket server,不是winsocket,这是一个websocketserver实现,同时也实现了http服务,对于单线程的个人应用非常适用
import web.socket.server;
var wsrv = web.socket.server();
//单独将http服务器提出来,方便操作
var srvHttp = wsrv.httpServer;
//将前面提取的系统“我的文档”路径设置为http根目录
srvHttp.documentRoot = winform.txtMessage.text;
//生成一个随机字符串作为用户访问令牌
srvHttp.userToken = string.random(18);
//将令牌字符串显示在界面的文本框中
winform.editPassword.text = srvHttp.userToken;

//此部分提取指定文件图标用于网页显示。
import fsys.info; 
import console;
var cacheSysIcons = {}
var getSysIconIndex = function(path){ 
    //获取指定文件的图标信息,如果不存在则返回。
    //文件的图标分为两种,一种是自身自带了图标文件,比如exe文件,另一种是系统针对扩展名指定的图标,如doc,所以参数给了两个
    //返回类型为fsys.info.shFileInfoObject 
    var sfi = fsys.info.get(path, 0x100/*_SHGFI_ICON*/ | 0x4000/*_SHGFI_SYSICONINDEX*/);
    if( !(sfi.returnValue ) ) {
        return; 
    }
    //如果图标属性不存在于缓存变量中,则将图标导出为png,保存到缓存变量中
    if(!cacheSysIcons[sfi.iIcon]){
        var dataUrl;
        var bmp = ..gdip.bitmap(sfi.hIcon,1/*_IMAGE_ICON*/);        
            if(bmp){
            cacheSysIcons[sfi.iIcon] = bmp.saveToBuffer(".png"); 
            bmp.dispose();
        }
    }
    //如果存在句柄,必须手动释放,具体参考fsys.info
    if(sfi.hIcon)::DestroyIcon(sfi.hIcon);    
    return sfi.iIcon;
}
//客户端ip地址缓存
var cacheClientIps = {}
//过程运行服务器,并指定http的路由响应过程
srvHttp.run( 
    function(response,request,session){ 
         //判断是否有token,同时支持会话和get参数
        var token = request.get["t"] : session["token"];
        //存在token且不一致,则返回一个未授权的错误给http客户端
        if( #srvHttp.userToken && (token != srvHttp.userToken) ){
            winform.txtMessage.printf("客户端:%s 连接被拒绝",request.remoteAddr);    
            response.errorStatus(401)
            return;
        }
        //将token存入会话变量,由于会话本身具有时长限制,长时间没有交互的客户端,会话变量会失效,需要重新带get参数访问才能长期有效
        session["token"] = token;
        //如果是新客户端,即ip不在缓存里诶包中,则显示客户端连接信息,并将ip加入缓存列表
        if(!cacheClientIps[request.remoteAddr]){
            winform.txtMessage.printf("客户端:%s 已连接",request.remoteAddr);    
            cacheClientIps[request.remoteAddr] = true;
        }
        //设置访问头,不进行限制
        response.headers["Access-Control-Allow-Origin"] = "*";
        response.headers["Access-Control-Allow-Headers"] = "*"
        //响应文件图标请求,请求参数icon为图标列表索引,即前述函数getSysIconIndex的返回值,shFileInfoObject.iIcon 
        //由于在生成文件列表的时候,已经将文件中的图标缓存到cacheSysIcons表变量中,此时直接进行提取即可
        //如果不存在则返回404
        if(request.path=="/main.aardio" && request.get["icon"]){
            var iconIdx = tonumber(request.get["icon"]);
            if(iconIdx!==null){
                if(cacheSysIcons[iconIdx]){
                    response.contentType = "image/png";
                    response.write(cacheSysIcons[iconIdx])
                    return;
                }
            }
            response.errorStatus(404);
            return;
        }
        //响应对上传文件夹的操作
        if(request.path=="/upload/main.aardio"){
        //响应删除已经上传到upload文件夹中的文件的请求
        //这个请求是由客户端引用的filepond脚本库实现并发起的,采用post方法提交要删除的文件
            if(request.method=="DELETE"){
                var path = request.postData();
                if(path && string.startWith(path,"/upload/")){
                    path = ..io.joinpath(srvHttp.documentRoot,path)
                    
                    if(io.exist(path)){
                        io.remove(path);
                        winform.txtMessage.print("已删除:" + path);
                        response.close();
                        return;    
                    }
                }
                
                response.errorStatus(404);
                return;
            }
        //响应上传文件的操作,参见fastcig.client.request.postFileData() 
        //返回的结果是fsys.multipartFormData类型
            fileData = request.postFileData()
            //如果存在提交文件数据,则创建文件夹并获取文件名并保存到上传文件夹中
            if(fileData){
                io.createDir(..io.joinpath(srvHttp.documentRoot,"upload"))
                winform.txtMessage.print(..io.joinpath(srvHttp.documentRoot,"upload"))
                
                var fileName = ..io.joinpath(srvHttp.documentRoot,"upload",fileData.filepond.filename) 
                var ok,err = fileData.filepond.save(fileName); 
                if(!ok){ response.error(err); }
                //向ui输出信息
                winform.txtMessage.text = 'http服务端已启动: \n'; 
                winform.txtMessage.print( srvHttp.getUrl(,true) + "/?t=" + srvHttp.userToken  );
    
                winform.txtMessage.print( "" );     
                winform.txtMessage.print( "上传成功:" + fileName );     
                //向客户端返回上传的文件名            
                response.contentType = "text/plain";
                response.write("/upload/",fileData.filepond.filename)
                return response.close() 
            }         
        }
        winform.txtMessage.print( request.path  );
        //如果请求文件不是文件夹则返回文件指定结果
        if(!fsys.isDir(request.path) ) {
            //此判断稍微复杂一些,要求请求的文件存在,且(不是ide环境 或者 请求的不是根目录的主程序)
            //也就是说:
            //1,首先文件要存在
            //2,不能请求的是主程序,这种情况只能存在于ide环境下
            //3,如果不是ide环境下,则没有请求当前主程序的情况,所以执行主目录下的main.aardio是可能的
            //默认情况下,arrdio的http服务器的默认文档是main.aardio,类似于其他web服务器的index.html
            //或者iis的default.html,查找顺序原因,需要做个判断
            //默认是可以执行aardio的cgi程序或者页面模板的,如果不是则直接返回文件本身。但对于直接下载大
            //文件来说,loadcode可能不是一个好的选择。
            
            if( ..io.exist(request.path) 
                && (!_STUDIO_INVOKED || request.path!="/main.aardio") )
                return response.loadcode(request.path)
            else {
                request.path = fsys.getParentDir(request.path)
            }
        } 
        //如果请求的是文件夹的情况下,则返回下列网页模版,输出文件清单
        response.write(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title>asynHttpServer - 扫码快传</title>
<link href="https://lib.baomitu.com/filepond/4.28.2/filepond.min.css" rel="stylesheet">
<script src="https://lib.baomitu.com/filepond/4.28.2/filepond.min.js"></script> 
<script>
(function (doc, win) {
    var docEl = doc.documentElement, 
        recalc = function () {
            var clientWidth = docEl.clientWidth;
            if (!clientWidth) return;
            
            clientWidth=(clientWidth>640)?640:clientWidth;
            docEl.style.fontSize = ( (docEl.clientWidth>docEl.clientHeight) ? 12 : 20) * (clientWidth / 320) + 'px';
        };
        
    if (!doc.addEventListener) return;
    //响应窗体尺寸代码
    win.addEventListener('orientationchange' in window ? 'orientationchange' : 'resize', recalc, false);
    doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
</script>
<style>
html {
    padding:20px 0 0;
}
li{ list-style-type:none; }
</style>
</head>
<body> 

<input type="file" class="filepond" name="filepond" multiple>
<script crossorigin="anonymous"> 
if(document.body.style.order === undefined){
    alert("浏览器版本过低,请使用Chrome或IE11以上版本浏览器打开此页面!")
}
//初始化filepond组件
var inputElement = document.querySelector('input[type="file"]');
FilePond.create(inputElement);
FilePond.setOptions({
    server: '/upload/?t=` + srvHttp.userToken + `',
    labelIdle: '拖放需要上传的文件到这里或者 <span class="filepond--label-action"> 浏览文件 </span>',
    labelFileProcessing: '上传中...',
    labelFileProcessingComplete: '上传成功'
});
//连接websocket服务器
websocket = new WebSocket("`+wsrv.getUrl("ws",true)+`");
//响应websocket消息更新页面,当服务器根目录被修改或者重启服务器时,服务器会发送reload消息
websocket.onmessage = function(evt) {
    if(evt.data=="reload"){
        window.location.pathname = "/";
        window.location.reload(true)
    }
};   
</script> 

<h2>当前目录:`
            ,request.path,`</h2><hr><ul>`)
        //上传文件夹如果存在置于第一个,并且单独替换名字
        if(request.path=="/" && ..io.exist("/upload/")){
            response.write('<li><img src="/?icon='++getSysIconIndex("/upload/")+'"><a href="/upload?t=' + srvHttp.userToken + '">上传目录</a><br>\r\n');    
        }
        //获取服务器根目录下的所有文件和文件夹
        var file,dir = fsys.list(request.path,,"*.*");
        //遍历文件夹,并排除上传文件夹,输出文件夹图标、路径,形成链接
        for(i=1;#dir;1){
            if(dir[i]==="upload" && request.path=="/") continue;
            
            var iconIdex = getSysIconIndex(dir[dir[ i ]])
            response.write('<li><img src="/?icon='++(iconIdex)+'"><a href="'
                ,inet.url.append(request.path,dir[ i ])
                ,'?t=' + srvHttp.userToken + '">',dir[ i ],'</a><br>\r\n');
        }
        //遍历文件,输出文件夹图标、路径,形成链接
        for(i=1;#file;1){
            var iconIdex = getSysIconIndex(file[file[ i ]])
            response.write('<li><img src="/?icon='++(iconIdex)+'"><a href="'
                    ,inet.url.append(request.path,file[ i ])
                    ,'?t=' + srvHttp.userToken + '">',file[ i ],'</a><br>\r\n');
        }
        
        response.write("</ul></body></html>")
    }     
);//http服务器响应结束
//根据服务器返回的信息,生成二维码和访问链接信息
import qrencode.bitmap;
var serverInfo = function(){
    var ip,port = srvHttp.getLocalIp();
    winform.editPort.text = port;
    winform.editDocumentRoot.text = io.fullpath(srvHttp.documentRoot)

    var url = srvHttp.getUrl(,true);
    if(#srvHttp.userToken){
        url = url + "/?t=" + srvHttp.userToken;
    } 
    
    winform.txtMessage.text = 'http服务端已启动: \n'; 
    winform.txtMessage.print(  url );
    //生成二维码
    var qrBmp = qrencode.bitmap( url );
    winform.qr.setBackground(qrBmp.copyBitmap(winform.qr.width)); 
        
    winform.txtMessage.print( 
        "手机扫码可自动打开此网页,可以方便地上传下载文件。
拖动文件或目录到窗口上客户端网页会自动刷新。

asynHttpServer 体积很小可嵌入任何 aardio 程序,
asynHttpServer 可以创建单线程异步模式的 HTTP 服务端,并可以同时创建 WebSocket 服务端(与HTTP服务端共享端口)。asynHttpServer 支持保持连接(Keep Alive),分块传输协议,支持断点续传,支持304缓存,支持文件表单上传,支持使用aardio编写的网站( 接口可兼容IIS/FastCGI下)。
"
    );     
}
//调用上面的函数
serverInfo();
//重启服务器
winform.btnStart.oncommand = function(id,event){
    winform.txtMessage.text = "";
    winform.btnStart.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
    win.delay(500);
 
    var port = tonumber(winform.editPort.text);
    srvHttp.documentRoot = fsys.isDir(winform.editDocumentRoot.text) ? winform.editDocumentRoot.text;
    srvHttp.userToken = winform.editPassword.text;
    srvHttp.start("0.0.0.0",port);
    serverInfo();
    
    winform.btnStart.disabledText = null;
}
//启用右键菜单、响应链接点击事件
import win.ui.menu
winform.txtMessage.enablePopMenu()
winform.txtMessage.onlink=function(message,href){
    if( message = 0x202/*_WM_LBUTTONUP*/ ) {
        import process;
        process.openUrl(href);
    }
}

//处理文件拖拽事件,将拖拽文件或者文件夹作为服务器根目录并刷新页面
winform.onDropFiles = function(files){
    var path = files[1]
    //如果不是文件夹则打开文件所在文件夹
    if(!fsys.isDir(path)){
        path = fsys.getParentDir(path)
    }
    winform.editDocumentRoot.text = path;
    srvHttp.documentRoot = path;
    config.winform.txtMessage = path;
    config.winform.save();
    
    wsrv.publish("reload");
}

import fsys.dlg.dir;
//处理设置主目录控件,打开目录打开对话框,处理内容同上
winform.btnOpen.oncommand = function(id,event){
    var dir = fsys.dlg.dir(winform.editDocumentRoot.text,winform)
    if(dir){
        winform.editDocumentRoot.text = dir;
        srvHttp.documentRoot = dir;
        
        config.winform.txtMessage = dir;
        config.winform.save();
        wsrv.publish("reload");
    }
}

import process;
//处理打开上传文件夹控件事件,建立并使用默认文件管理程序打开主目录下的upload目录
winform.btnOpenUpload.oncommand = function(id,event){
    var path = io.joinpath(winform.editDocumentRoot.text,"upload")
    if(io.createDir(path)){
        process.explore(path)
    }
}
//使用简单窗体
import win.ui.simpleWindow2;
win.ui.simpleWindow2(winform);
winform.show(); 
//设置配置文件夹为隐藏状态
import fsys;
fsys.attrib("/config/",,2/*_FILE_ATTRIBUTE_HIDDEN*/)

win.loopMessage();

 

  1 import fonts.fontAwesome;
  2 import win.ui;
  3 /*DSG{{*/
  4 var winform = win.form(text="asynHttpServer - 异步HTTP服务器";right=807;bottom=465;bgcolor=16777215;border="none";max=false)
  5 winform.add(
  6 bk={cls="bk";left=-2;top=-5;right=810;bottom=29;bgcolor=12639424;z=9};
  7 bkplus={cls="bkplus";text="asynHttpServer - 扫码传文件";left=18;top=4;right=203;bottom=26;align="left";color=5921370;z=10};
  8 btnOpen={cls="plus";text='\uF115';left=444;top=50;right=479;bottom=75;dr=1;dt=1;font=LOGFONT(h=-16;name='FontAwesome');notify=1;z=5};
  9 btnOpenUpload={cls="plus";text="打开上传目录";left=568;top=429;right=709;bottom=458;dr=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome');padding={left=8;top=2}};iconText='\uF115';notify=1;textPadding={left=20};z=11};
 10 btnStart={cls="plus";text="重启服务";left=655;top=47;right=755;bottom=76;bgcolor=14935259;dr=1;dt=1;font=LOGFONT(h=-15);iconStyle={align="left";font=LOGFONT(h=-15;name='FontAwesome');padding={left=8;top=2}};iconText='\uF233';notify=1;textPadding={left=20};z=4};
 11 editDocumentRoot={cls="plus";left=131;top=49;right=430;bottom=73;align="right";border={bottom=1;color=-8355712};dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-16);z=7};
 12 editPassword={cls="plus";left=441;top=84;right=632;bottom=108;align="left";border={bottom=1;color=-8355712};dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-16);z=13};
 13 editPort={cls="plus";left=550;top=49;right=628;bottom=73;align="left";border={bottom=1;color=-8355712};dl=1;dr=1;dt=1;editable=1;font=LOGFONT(h=-16);z=8};
 14 plus={cls="plus";text="访问密码:";left=332;top=90;right=433;bottom=114;align="right";dr=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=12};
 15 qr={cls="plus";left=499;top=132;right=760;bottom=418;db=1;dr=1;dt=1;repeat="scale";z=6};
 16 static={cls="plus";text="端口:";left=484;top=52;right=547;bottom=76;align="right";dr=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=2};
 17 static2={cls="plus";text="网站根目录:";left=15;top=52;right=129;bottom=76;align="right";dl=1;dt=1;font=LOGFONT(h=-15);transparent=1;z=3};
 18 txtMessage={cls="richedit";left=42;top=132;right=469;bottom=418;autohscroll=false;db=1;dl=1;dr=1;dt=1;link=1;multiline=1;vscroll=1;z=1}
 19 )
 20 /*}}*/
 21 
 22 winform.btnStart.skin( {
 23     background={
 24         default=0x668FB2B0;
 25         hover=0xFF928BB3;
 26         disabled=0xFFCCCCCC; 
 27     }
 28 })
 29 
 30 winform.btnOpen.skin( {
 31     background={
 32         default=0;
 33         hover=0xFF928BB3;
 34         disabled=0xFFCCCCCC; 
 35     }
 36 })
 37 
 38 winform.btnOpenUpload.skin( {
 39     background={
 40         default=0;
 41         hover=0xFF928BB3;
 42         disabled=0xFFCCCCCC; 
 43     }
 44 })
 45 
 46 //从配置文件中读取文本信息标题,如果有写入文本信息框,如果没有获取系统“我的文档”文件夹路径并写入文本信息框
 47 //在这里,作者没有定义一个专用的变量,而是直接使用文本信息框的属性进行临时的文本存储,这种方式在后面反复出现
 48 //这种方式可能是为了节省资源,但造成可读性差。并且,只有在顺序执行的过程中才可以用。
 49 import fsys.config;
 50 config = fsys.config("/config/"); 
 51 if( io.exist(config.winform.txtMessage) ){
 52     winform.txtMessage.text = config.winform.txtMessage;
 53 }
 54 else {
 55     //获取系统“我的文档”路径,通过ide智能提示,还可以获得诸如系统文件夹、桌面、appdata等特殊文件夹的路径
 56     winform.txtMessage.text = io.getSpecial(0x5/*_CSIDL_MYDOCUMENTS*/)
 57     
 58 }
 59 
 60 //请注意此处引入的直接是WebSocket server,不是winsocket,这是一个websocketserver实现,同时也实现了http服务,对于单线程的个人应用非常适用
 61 import web.socket.server;
 62 var wsrv = web.socket.server();
 63 //单独将http服务器提出来,方便操作
 64 var srvHttp = wsrv.httpServer;
 65 //将前面提取的系统“我的文档”路径设置为http根目录
 66 srvHttp.documentRoot = winform.txtMessage.text;
 67 //生成一个随机字符串作为用户访问令牌
 68 srvHttp.userToken = string.random(18);
 69 //将令牌字符串显示在界面的文本框中
 70 winform.editPassword.text = srvHttp.userToken;
 71 
 72 //此部分提取指定文件图标用于网页显示。
 73 import fsys.info; 
 74 import console;
 75 var cacheSysIcons = {}
 76 var getSysIconIndex = function(path){ 
 77     //获取指定文件的图标信息,如果不存在则返回。
 78     //文件的图标分为两种,一种是自身自带了图标文件,比如exe文件,另一种是系统针对扩展名指定的图标,如doc,所以参数给了两个
 79     //返回类型为fsys.info.shFileInfoObject 
 80     var sfi = fsys.info.get(path, 0x100/*_SHGFI_ICON*/ | 0x4000/*_SHGFI_SYSICONINDEX*/);
 81     if( !(sfi.returnValue ) ) {
 82         return; 
 83     }
 84     //如果图标属性不存在于缓存变量中,则将图标导出为png,保存到缓存变量中
 85     if(!cacheSysIcons[sfi.iIcon]){
 86         var dataUrl;
 87         var bmp = ..gdip.bitmap(sfi.hIcon,1/*_IMAGE_ICON*/);        
 88             if(bmp){
 89             cacheSysIcons[sfi.iIcon] = bmp.saveToBuffer(".png"); 
 90             bmp.dispose();
 91         }
 92     }
 93     //如果存在句柄,必须手动释放,具体参考fsys.info
 94     if(sfi.hIcon)::DestroyIcon(sfi.hIcon);    
 95     return sfi.iIcon;
 96 }
 97 //客户端ip地址缓存
 98 var cacheClientIps = {}
 99 //过程运行服务器,并指定http的路由响应过程
100 srvHttp.run( 
101     function(response,request,session){ 
102          //判断是否有token,同时支持会话和get参数
103         var token = request.get["t"] : session["token"];
104         //存在token且不一致,则返回一个未授权的错误给http客户端
105         if( #srvHttp.userToken && (token != srvHttp.userToken) ){
106             winform.txtMessage.printf("客户端:%s 连接被拒绝",request.remoteAddr);    
107             response.errorStatus(401)
108             return;
109         }
110         //将token存入会话变量,由于会话本身具有时长限制,长时间没有交互的客户端,会话变量会失效,需要重新带get参数访问才能长期有效
111         session["token"] = token;
112         //如果是新客户端,即ip不在缓存里诶包中,则显示客户端连接信息,并将ip加入缓存列表
113         if(!cacheClientIps[request.remoteAddr]){
114             winform.txtMessage.printf("客户端:%s 已连接",request.remoteAddr);    
115             cacheClientIps[request.remoteAddr] = true;
116         }
117         //设置访问头,不进行限制
118         response.headers["Access-Control-Allow-Origin"] = "*";
119         response.headers["Access-Control-Allow-Headers"] = "*"
120         //响应文件图标请求,请求参数icon为图标列表索引,即前述函数getSysIconIndex的返回值,shFileInfoObject.iIcon 
121         //由于在生成文件列表的时候,已经将文件中的图标缓存到cacheSysIcons表变量中,此时直接进行提取即可
122         //如果不存在则返回404
123         if(request.path=="/main.aardio" && request.get["icon"]){
124             var iconIdx = tonumber(request.get["icon"]);
125             if(iconIdx!==null){
126                 if(cacheSysIcons[iconIdx]){
127                     response.contentType = "image/png";
128                     response.write(cacheSysIcons[iconIdx])
129                     return;
130                 }
131             }
132             response.errorStatus(404);
133             return;
134         }
135         //响应对上传文件夹的操作
136         if(request.path=="/upload/main.aardio"){
137         //响应删除已经上传到upload文件夹中的文件的请求
138         //这个请求是由客户端引用的filepond脚本库实现并发起的,采用post方法提交要删除的文件
139             if(request.method=="DELETE"){
140                 var path = request.postData();
141                 if(path && string.startWith(path,"/upload/")){
142                     path = ..io.joinpath(srvHttp.documentRoot,path)
143                     
144                     if(io.exist(path)){
145                         io.remove(path);
146                         winform.txtMessage.print("已删除:" + path);
147                         response.close();
148                         return;    
149                     }
150                 }
151                 
152                 response.errorStatus(404);
153                 return;
154             }
155         //响应上传文件的操作,参见fastcig.client.request.postFileData() 
156         //返回的结果是fsys.multipartFormData类型
157             fileData = request.postFileData()
158             //如果存在提交文件数据,则创建文件夹并获取文件名并保存到上传文件夹中
159             if(fileData){
160                 io.createDir(..io.joinpath(srvHttp.documentRoot,"upload"))
161                 winform.txtMessage.print(..io.joinpath(srvHttp.documentRoot,"upload"))
162                 
163                 var fileName = ..io.joinpath(srvHttp.documentRoot,"upload",fileData.filepond.filename) 
164                 var ok,err = fileData.filepond.save(fileName); 
165                 if(!ok){ response.error(err); }
166                 //向ui输出信息
167                 winform.txtMessage.text = 'http服务端已启动: \n'; 
168                 winform.txtMessage.print( srvHttp.getUrl(,true) + "/?t=" + srvHttp.userToken  );
169     
170                 winform.txtMessage.print( "" );     
171                 winform.txtMessage.print( "上传成功:" + fileName );     
172                 //向客户端返回上传的文件名            
173                 response.contentType = "text/plain";
174                 response.write("/upload/",fileData.filepond.filename)
175                 return response.close() 
176             }         
177         }
178         winform.txtMessage.print( request.path  );
179         //如果请求文件不是文件夹则返回文件指定结果
180         if(!fsys.isDir(request.path) ) {
181             //此判断稍微复杂一些,要求请求的文件存在,且(不是ide环境 或者 请求的不是根目录的主程序)
182             //也就是说:
183             //1,首先文件要存在
184             //2,不能请求的是主程序,这种情况只能存在于ide环境下
185             //3,如果不是ide环境下,则没有请求当前主程序的情况,所以执行主目录下的main.aardio是可能的
186             //默认情况下,arrdio的http服务器的默认文档是main.aardio,类似于其他web服务器的index.html
187             //或者iis的default.html,查找顺序原因,需要做个判断
188             //默认是可以执行aardio的cgi程序或者页面模板的,如果不是则直接返回文件本身。但对于直接下载大
189             //文件来说,loadcode可能不是一个好的选择。
190             
191             if( ..io.exist(request.path) 
192                 && (!_STUDIO_INVOKED || request.path!="/main.aardio") )
193                 return response.loadcode(request.path)
194             else {
195                 request.path = fsys.getParentDir(request.path)
196             }
197         } 
198         //如果请求的是文件夹的情况下,则返回下列网页模版,输出文件清单
199         response.write(`
200 <!doctype html>
201 <html>
202 <head>
203 <meta charset="utf-8">
204 <meta http-equiv="X-UA-Compatible" content="IE=edge" />
205 <title>asynHttpServer - 扫码快传</title>
206 <link href="https://lib.baomitu.com/filepond/4.28.2/filepond.min.css" rel="stylesheet">
207 <script src="https://lib.baomitu.com/filepond/4.28.2/filepond.min.js"></script> 
208 <script>
209 (function (doc, win) {
210     var docEl = doc.documentElement, 
211         recalc = function () {
212             var clientWidth = docEl.clientWidth;
213             if (!clientWidth) return;
214             
215             clientWidth=(clientWidth>640)?640:clientWidth;
216             docEl.style.fontSize = ( (docEl.clientWidth>docEl.clientHeight) ? 12 : 20) * (clientWidth / 320) + 'px';
217         };
218         
219     if (!doc.addEventListener) return;
220     //响应窗体尺寸代码
221     win.addEventListener('orientationchange' in window ? 'orientationchange' : 'resize', recalc, false);
222     doc.addEventListener('DOMContentLoaded', recalc, false);
223 })(document, window);
224 </script>
225 <style>
226 html {
227     padding:20px 0 0;
228 }
229 li{ list-style-type:none; }
230 </style>
231 </head>
232 <body> 
233 
234 <input type="file" class="filepond" name="filepond" multiple>
235 <script crossorigin="anonymous"> 
236 if(document.body.style.order === undefined){
237     alert("浏览器版本过低,请使用Chrome或IE11以上版本浏览器打开此页面!")
238 }
239 //初始化filepond组件
240 var inputElement = document.querySelector('input[type="file"]');
241 FilePond.create(inputElement);
242 FilePond.setOptions({
243     server: '/upload/?t=` + srvHttp.userToken + `',
244     labelIdle: '拖放需要上传的文件到这里或者 <span class="filepond--label-action"> 浏览文件 </span>',
245     labelFileProcessing: '上传中...',
246     labelFileProcessingComplete: '上传成功'
247 });
248 //连接websocket服务器
249 websocket = new WebSocket("`+wsrv.getUrl("ws",true)+`");
250 //响应websocket消息更新页面,当服务器根目录被修改或者重启服务器时,服务器会发送reload消息
251 websocket.onmessage = function(evt) {
252     if(evt.data=="reload"){
253         window.location.pathname = "/";
254         window.location.reload(true)
255     }
256 };   
257 </script> 
258 
259 <h2>当前目录:`
260             ,request.path,`</h2><hr><ul>`)
261         //上传文件夹如果存在置于第一个,并且单独替换名字
262         if(request.path=="/" && ..io.exist("/upload/")){
263             response.write('<li><img src="/?icon='++getSysIconIndex("/upload/")+'"><a href="/upload?t=' + srvHttp.userToken + '">上传目录</a><br>\r\n');    
264         }
265         //获取服务器根目录下的所有文件和文件夹
266         var file,dir = fsys.list(request.path,,"*.*");
267         //遍历文件夹,并排除上传文件夹,输出文件夹图标、路径,形成链接
268         for(i=1;#dir;1){
269             if(dir[i]==="upload" && request.path=="/") continue;
270             
271             var iconIdex = getSysIconIndex(dir[dir[ i ]])
272             response.write('<li><img src="/?icon='++(iconIdex)+'"><a href="'
273                 ,inet.url.append(request.path,dir[ i ])
274                 ,'?t=' + srvHttp.userToken + '">',dir[ i ],'</a><br>\r\n');
275         }
276         //遍历文件,输出文件夹图标、路径,形成链接
277         for(i=1;#file;1){
278             var iconIdex = getSysIconIndex(file[file[ i ]])
279             response.write('<li><img src="/?icon='++(iconIdex)+'"><a href="'
280                     ,inet.url.append(request.path,file[ i ])
281                     ,'?t=' + srvHttp.userToken + '">',file[ i ],'</a><br>\r\n');
282         }
283         
284         response.write("</ul></body></html>")
285     }     
286 );//http服务器响应结束
287 //根据服务器返回的信息,生成二维码和访问链接信息
288 import qrencode.bitmap;
289 var serverInfo = function(){
290     var ip,port = srvHttp.getLocalIp();
291     winform.editPort.text = port;
292     winform.editDocumentRoot.text = io.fullpath(srvHttp.documentRoot)
293 
294     var url = srvHttp.getUrl(,true);
295     if(#srvHttp.userToken){
296         url = url + "/?t=" + srvHttp.userToken;
297     } 
298     
299     winform.txtMessage.text = 'http服务端已启动: \n'; 
300     winform.txtMessage.print(  url );
301     //生成二维码
302     var qrBmp = qrencode.bitmap( url );
303     winform.qr.setBackground(qrBmp.copyBitmap(winform.qr.width)); 
304         
305     winform.txtMessage.print( 
306         "手机扫码可自动打开此网页,可以方便地上传下载文件。
307 拖动文件或目录到窗口上客户端网页会自动刷新。
308 
309 asynHttpServer 体积很小可嵌入任何 aardio 程序,
310 asynHttpServer 可以创建单线程异步模式的 HTTP 服务端,并可以同时创建 WebSocket 服务端(与HTTP服务端共享端口)。asynHttpServer 支持保持连接(Keep Alive),分块传输协议,支持断点续传,支持304缓存,支持文件表单上传,支持使用aardio编写的网站( 接口可兼容IIS/FastCGI下)。
311 "
312     );     
313 }
314 //调用上面的函数
315 serverInfo();
316 //重启服务器
317 winform.btnStart.oncommand = function(id,event){
318     winform.txtMessage.text = "";
319     winform.btnStart.disabledText = {'\uF254';'\uF251';'\uF252';'\uF253';'\uF250'}
320     win.delay(500);
321  
322     var port = tonumber(winform.editPort.text);
323     srvHttp.documentRoot = fsys.isDir(winform.editDocumentRoot.text) ? winform.editDocumentRoot.text;
324     srvHttp.userToken = winform.editPassword.text;
325     srvHttp.start("0.0.0.0",port);
326     serverInfo();
327     
328     winform.btnStart.disabledText = null;
329 }
330 //启用右键菜单、响应链接点击事件
331 import win.ui.menu
332 winform.txtMessage.enablePopMenu()
333 winform.txtMessage.onlink=function(message,href){
334     if( message = 0x202/*_WM_LBUTTONUP*/ ) {
335         import process;
336         process.openUrl(href);
337     }
338 }
339 
340 //处理文件拖拽事件,将拖拽文件或者文件夹作为服务器根目录并刷新页面
341 winform.onDropFiles = function(files){
342     var path = files[1]
343     //如果不是文件夹则打开文件所在文件夹
344     if(!fsys.isDir(path)){
345         path = fsys.getParentDir(path)
346     }
347     winform.editDocumentRoot.text = path;
348     srvHttp.documentRoot = path;
349     config.winform.txtMessage = path;
350     config.winform.save();
351     
352     wsrv.publish("reload");
353 }
354 
355 import fsys.dlg.dir;
356 //处理设置主目录控件,打开目录打开对话框,处理内容同上
357 winform.btnOpen.oncommand = function(id,event){
358     var dir = fsys.dlg.dir(winform.editDocumentRoot.text,winform)
359     if(dir){
360         winform.editDocumentRoot.text = dir;
361         srvHttp.documentRoot = dir;
362         
363         config.winform.txtMessage = dir;
364         config.winform.save();
365         wsrv.publish("reload");
366     }
367 }
368 
369 import process;
370 //处理打开上传文件夹控件事件,建立并使用默认文件管理程序打开主目录下的upload目录
371 winform.btnOpenUpload.oncommand = function(id,event){
372     var path = io.joinpath(winform.editDocumentRoot.text,"upload")
373     if(io.createDir(path)){
374         process.explore(path)
375     }
376 }
377 //使用简单窗体
378 import win.ui.simpleWindow2;
379 win.ui.simpleWindow2(winform);
380 winform.show(); 
381 //设置配置文件夹为隐藏状态
382 import fsys;
383 fsys.attrib("/config/",,2/*_FILE_ATTRIBUTE_HIDDEN*/)
384 
385 win.loopMessage();

 

上面是我在读代码的一些补充注释,有些其实是废话,看代码显而易见,有些则是不那么明显的,有一些默认的设置需要查询参考才能获知,还有一些是不是很直白,需要绕一下才能理解的业务逻辑,分别做了解释。
posted @ 2022-06-05 12:11  首席吐槽官秦寿  阅读(742)  评论(0编辑  收藏  举报