socket实现简陋远程文件管理

By money at 2022-01-18 • 4人收藏 • 1667人看过

最近需要对多台远程设备文件统一管理,简单撸了一套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连接:


image.png

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,并枚举盘符发送给服务端

image.png

点击盘符,开始枚举目录及文件
image.png

点击目录,继续向下枚举,两个小点返回上级,跟其它网页版文件管理器大致相同

点击文件,获取文件的信息(只取了修改时间和文件大小)

image.png

点击下载,server新建一条tcp通道,并通知client连接新端口,连接上后自动传输文件,文件传输完成后,自动打开所在路径

image.png


此代码仅供学习讨论,请勿用于非法用途,后果自负,与本人无关

5 个回复 | 最后更新于 2022-11-08
2022-01-18   #1

很实用,赞一个

2022-01-21   #2

感谢分享

2022-10-07   #3

大佬,能否方便,分享一下hpsocket.httpserverex库,用jsonserver代替跑不起来

2022-11-08   #4

受这个帖子的启发,用Golang写了一个文件预览服务器,只要浏览器支持的格式都可以直接打开预览,包括文字、图片、音频、视频、pdf等等。

screenshots.gif

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>


2022-11-08   #5

回复#4 @jerryxjr1220 :

强,学习了

登录后方可回帖

登 录
信息栏
 私人小站

本站域名

ChengXu.XYZ

投诉联系:  popdes@126.com



快速上位机开发学习,本站主要记录了学习过程中遇到的问题和解决办法及上位机代码分享

这里主要专注于学习交流和经验分享.
纯私人站,当笔记本用的,学到哪写到哪.
如果侵权,联系 Popdes@126.com

友情链接
Aardio官方
Aardio资源网


才仁机械


网站地图SiteMap

Loading...