socket实现简陋远程文件管理
By
money
at 2022-01-18 • 4人收藏 • 1740人看过
最近需要对多台远程设备文件统一管理,简单撸了一套CS代码。
server端
import win.ui; /*DSG{{*/ var winform = win.form(text="aardio form";right=759;bottom=469) winform.add() /*}}*/ import web.view; import fsys; import fsys.file; import win.dlg.message; import hpsocket.httpServerEx; var hpHttpServer = hpsocket.httpServerEx(); hpHttpServer.threadGlobal = { winform = winform; rpcexternal ={ $callWorker = function($, data){ data.socket=$; thread.command.post(data.action, data) } } } //监听线程被创建后触发 hpHttpServer.onThreadCreated = function(){ import thread.command; import time.performance; import string.xxtea; import console; } //监听线程退出前触发此事件 hpHttpServer.onThreadEnd = function(){} //允许升级为WebSocket协议 hpHttpServer.onUpgrade = function(hpHttpServer,connId,upgradeType){ if(upgradeType!=1) return -1; hpHttpServer.sendWsSwitchingProtocols(connId); hpHttpServer.notify(connId, "rpcClientId", connId) } hpHttpServer.onWsMessageHeader = function(hpHttpServer,connId,final,reserved,opCode,mask,bodyLen){ hpHttpServer.reallocString(connId,bodyLen);//初始化缓冲区 } //接收到WebSocket请求数据 hpHttpServer.onWsMessageBody = function(hpHttpServer,connId,pData,len){ hpHttpServer.appendString(connId,pData,len); } hpHttpServer.afterJsonStringify = function(jsonData){ console.dump("afterJsonStringify", #jsonData) return jsonData; } hpHttpServer.beforeJsonParse = function(connId, jsonData){ return jsonData; } //WebSocket请求数据接收完成 hpHttpServer.onWsMessageComplete = function(hpHttpServer,connId){ var data = hpHttpServer.getString(connId); if( data ){ hpHttpServer._translateMessage(connId, data); } } hpHttpServer.onClose = function(hpHttpServer,connId,enOperation,errCode){ hpHttpServer.reallocString(connId,0);//释放缓冲区 thread.command.post("onClientClose", connId); } sleep(1000) import wsock; var port = wsock.getFreePort() port=8999 hpHttpServer.start(/*IP*/,port); import console console.dump("已启动:",hpHttpServer.getWsUrl()) var wb = web.view(winform); var dirs={}; newFileReader = function(id, f){ var port = wsock.getFreePort() thread.create(function(id, port, f){ import wsock.tcp.server; import wsock.tcp.client; import thread.command; import fsys; import math; import console; console.dump("文件线程已经启动,端口:", port) var tcpServer = wsock.tcp.server("0.0.0.0",port) tcpServer.forever( function(acceptSocket){ console.dump("客户端连接") var client = wsock.tcp.client(,acceptSocket); var file = io.open(f,"w+b");//注意io.open默认是文本方式写入的,b指定二进制模式 var size = math.size64(); var buffer = raw.buffer(0x4000); for(readSize,remainSize in client.eachReadBuffer(buffer) ){ size.add(readSize); console.dump( size.format()) thread.command.post("downloadSize", id, size.format()) file.writeBuffer(buffer,readSize); } file.close(); client.close(); tcpServer.close(); } ) console.dump("文件线程关闭") },id, port, f) return port; } var download={} wb.export ({ enumDir = function(path){ hpHttpServer.publish("enumDir", path) }; fileInfo = function(id, path){ console.dump(id) hpHttpServer.publish("fileInfo", id, path) }; enumParent = function(path){ path = fsys.getParentDir(path); hpHttpServer.publish("enumDir", path) } download = function(id, path){ var f = io.fullpath("/user_download/"+string.replace(path,"@@:",'')) if(io.exist(f)){ if(!winform.msgAsk("已经存在此文件,是否重新下载?")){ process.exploreSelect(f); return ; } } //先创建一个空文件及对应的完整目录 string.save(f, '') var port = newFileReader(id, f) download[path] = f; hpHttpServer.publish("download", id, path, port) } }) wb.html = /** <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>文件浏览</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Bootstrap --> <link href="https://cdn.staticfile.org/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" media="screen"> <style> #fork{position:fixed;top:0;right:0;_position:absolute;z-index: 10000;} .bottom{margin: 20px auto; width: 100%; text-align: center;} .container{width: 1080px; margin: 50px auto;} </style> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> <script src="https://layui.11dz.cn/layui/release/layer/dist/layer.js"></script> <head> <body> <script> parentPath=""; layerIndex=0; setVolume = function(data){ var html = "文件浏览"; data.forEach(function(item){ html += '<a class="driver" path="'+item+'" href="javascript:void(0);">'+item+'/</a> ' }) document.getElementById("h1").innerHTML = html; $(".driver").off("click"); $(".driver").on("click", function(){ loading() parentPath = $(this).attr("path") enumDir($(this).attr("path")) }); } bindDownClick = function(cls){ setTimeout(function(){ $(".down").off("click"); $(".down").on("click", function(){ var file = parentPath + $(this).attr("path") var id = $(this).data("id") download(id, file); }); },100) } downloadSize = function(id, size){ var s = $("#s_"+id) s.text(s.data("size")+"/"+size) } downEnd = function(id){ var s = $("#d_"+id) s.text("已下载") } setFileInfo = function(id, time, size){ loadend() var path = $("#"+id).attr("path") var txt = $("#"+id).text() document.getElementById(id).outerHTML = '<a id="'+id+' class="file" path="'+path+'" href="javascript:void(0);">'+txt+'</a> '+time+' <span id="s_'+id+'" data-size='+size+'>'+size+'</span> <a id="d_'+id+'" data-id="'+id+'" path="'+path+'" class="down" href="javascript:void(0);" >下载</a>' bindClick('file') bindDownClick() } loading = function(){ layerIndex = layer.load(1, { shade: [0.1,'#fff'] //0.1透明度的白色背景 }); } loadend = function(){ layer.close(layerIndex) } bindClick = function(cls){ setTimeout(function(){ $("."+cls).off("click"); $("."+cls).on("click", function(){ loading() if(cls=="dir"){ if($(this).text()!=".."){ parentPath += $(this).attr("path") } enumDir(parentPath) }else if(cls=="file"){ var file = parentPath + $(this).attr("path") fileInfo($(this).attr("id"), file) } }); $(".parent").off("click"); $(".parent").on("click", function(){ loading() enumParent(parentPath) }); },100) } setRow = function(parent, data, cls, clear){ parentPath = parent; loadend() var html = document.getElementById("pre").innerHTML; if(clear){ html = ""; } if(parentPath.length>3 && cls=="dir"){ html = '<a class="parent" href="javascript:void(0);">..</a> \r\n'; } data.forEach(function(item,id){ if(cls=="dir"){ html += '<a class="'+cls+'" path="/'+item+'" href="javascript:void(0);">/'+item+'</a> \r\n' }else { html += '<a id="f_'+id+'" class="'+cls+'" path="/'+item+'" href="javascript:void(0);">/'+item+'</a> \r\n' } }) document.getElementById("pre").innerHTML = html; bindClick(cls) document.getElementById("h2").innerHTML ="路径:"+parentPath; } </script> <div class="container"> <h1 id="h1">文件浏览</h1> <h2 id="h2"></h2> <pre id="pre"> </pre> </div> <div class="bottom"> </div> </body> </html> **/ import thread.command; import process; var cmd = thread.command(winform); cmd.setVolume = function(data){ var json = web.json.stringify(data.data) wb.doScript("setVolume("+json+")") } cmd.enumDir = function(data){ var json = web.json.stringify(data.dir) wb.doScript("setRow('"+data.path+"',"+json+", 'dir', true)") json = web.json.stringify(data.file) wb.doScript("setRow('"+data.path+"',"+json+", 'file', false)") } cmd.fileInfo = function(data){ if(data.sc){ var js = string.format("setFileInfo('%s','%s','%s')", data.id, data.writeTime, data.size) wb.doScript(js) }else { win.msgbox(data.msg); wb.doScript("loadend()") } } cmd.downloadSize = function(id, size){ var js = string.format("downloadSize('%s','%s')", id, size) //console.dump(js) wb.doScript(js) } cmd.download = function(data){ if(!data.sc){ win.msgbox(data.msg); }else { process.exploreSelect(download[data.path]); } var js = string.format("downEnd('%s')", data.id) console.dump(js) wb.doScript(js) } winform.show(); win.loopMessage(); return winform;
代码中有自定义库hpsocket.httpServerEx,作用与web.socket.jsonServer功能一致,可替换掉运行。
启动后这样,等待client连接:
client端:
import win import sys.volume; import web.socket.jsonClient; import fsys; import fsys.file; if(_STUDIO_INVOKED){ import console console.open() } var dyws = web.socket.jsonClient(); var commonHost = "*********"; if(_STUDIO_INVOKED){ commonHost = "127.0.0.1:8999"; } var wsServer = "ws://"+commonHost+"/jsonrpc" connectServer = function(){ win.setTimeout( function(){ //console.dump(wsServer) if(!userClash){ dyws.connect(wsServer); } },1000 ) } dyws.on("open",function(){ //console.dump(open) var json = {} var drives = sys.volume.getLogicalDrives() for(i,drv in drives){ var info = sys.volume.getInfo( drv) ; if(info){ table.push(json, info.drive) } } dyws.$callWorker({ action="setVolume"; data=json }); }) dyws.on("enumDir",function(path){ //console.dump("path", path) var file, dir = fsys.list(path,,"*.*") dyws.$callWorker({ action="enumDir"; file=file; dir=dir; path=path; }); }) dyws.on("fileInfo",function(id, path){ //console.dump("path", id, path) var file = fsys.file(path) if(file){ dyws.$callWorker({ action="fileInfo"; writeTime=tostring(file.getTime().write); size=fsys.formatSize(file.size64()); id=id; sc=true }); }else { dyws.$callWorker({ action="fileInfo"; writeTick=tonumber(file.getTime().write); id=id; sc=false; msg="打开文件失败" }); } }) dyws.on("download",function(id, path, port){ var ret,err = thread.invokeAndWait( function(path, port){ import wsock.tcp.client; var socket = wsock.tcp.client() var server = "*********"; if(_STUDIO_INVOKED){ server = "127.0.0.1"; } if(socket.connect(server,port) ){ var file,err = io.open(path,"rb") if( file ){ while( var buf; buf,readSize = file.read(4096); buf ) { socket.writeBuffer(buf,readSize) ; } file.close(); return true, "下载完成"; }else { return false, "打开文件失败"; } }else { return false, "连接失败"; } }, path, port ) dyws.$callWorker({ action="download"; path=path; id=id; sc=ret; msg=err }); }) connectServer() win.loopMessage()
clinet启动后,连接到server,并枚举盘符发送给服务端
点击盘符,开始枚举目录及文件
点击目录,继续向下枚举,两个小点返回上级,跟其它网页版文件管理器大致相同
点击文件,获取文件的信息(只取了修改时间和文件大小)
点击下载,server新建一条tcp通道,并通知client连接新端口,连接上后自动传输文件,文件传输完成后,自动打开所在路径
此代码仅供学习讨论,请勿用于非法用途,后果自负,与本人无关
5 个回复 | 最后更新于 2022-11-08
2022-11-08
#4
受这个帖子的启发,用Golang写了一个文件预览服务器,只要浏览器支持的格式都可以直接打开预览,包括文字、图片、音频、视频、pdf等等。
package main import ( "fmt" "github.com/gin-gonic/gin" "net/http" "os" "strings" "time" ) func main() { gs := gin.Default() gs.LoadHTMLGlob("./template/*") gs.GET("/", func(c *gin.Context) { var dirList []map[string]string GetAllFiles("rootpath/", &dirList) c.HTML(http.StatusOK, "index.html", gin.H{ "path": dirList, }) }) gs.GET("/askfor/rootpath/*path", func(c *gin.Context) { path, _ := c.Params.Get("path") //fmt.Println(path) fpath := "./rootpath" + path if strings.HasSuffix(fpath, "/") { var dirList []map[string]string GetAllFiles("rootpath/", &dirList) c.HTML(http.StatusOK, "index.html", gin.H{ "path": dirList, }) return } fs, err := os.Open(fpath) if err != nil { fmt.Println(err) return } defer fs.Close() http.ServeContent(c.Writer, c.Request, "file", time.Now(), fs) }) gs.Run(":9981") } func GetAllFiles(pathStr string, dirList *[]map[string]string) { fs, _ := os.ReadDir(pathStr) for _, f := range fs { if f.IsDir() { *dirList = append(*dirList, map[string]string{"name": f.Name(), "value": pathStr + f.Name() + "/"}) GetAllFiles(pathStr+f.Name()+"/", dirList) } else { *dirList = append(*dirList, map[string]string{"name": f.Name(), "value": pathStr + f.Name()}) } } }
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>File Review</title> </head> <body> <div> <p>根目录: <a href="/">root</a></p> </div> <div> <table> <thead> <tr><td style="width: 100px">名称</td><td>路径</td></tr> </thead> <tbody> {{range $idx, $item := .path}} <tr><td>{{$item.name}}</td> <td><a href = '/askfor/{{$item.value}}' >{{$item.value}}</a></td></tr> {{end}} </tbody> </table> </div> </body> </html>
登录后方可回帖
很实用,赞一个