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();
上面是我在读代码的一些补充注释,有些其实是废话,看代码显而易见,有些则是不那么明显的,有一些默认的设置需要查询参考才能获知,还有一些是不是很直白,需要绕一下才能理解的业务逻辑,分别做了解释。