此工具是纯PHP项目,基于GatewayWorker与workerman编写而成
先看效果
https://player.bilibili.com/player.html?aid=577313164&bvid=BV1VB4y1o7WN&cid=1303068969&p=1
支持文件下载(只是大文件下载的会有点慢)
有三个文件,分别为server_listen、server_connect、local_connect
其中server_listen部署在服务器,开启一个端口监听来自server_connect客户端发来的请求,以及返回local_connect设备上的数据信息
server_listen、server_connect部署在服务器上,local_connect部署在需要暴漏在公网的计算机设备上
server_listen监听一个websocket服务
server_connect通过前端js连接server_listen所监听的端口
local_connect也连接server_listen所监听的端口
从而致使用户通过server_connect的web界面与local_connect所部署的计算机产生了连接
server_listen核心代码:
<?php
namespace plugin\webman\gateway;
use GatewayWorker\Lib\Gateway;
use think\facade\Cache;
use Workerman\Connection\TcpConnection;
class Events{
public static $initialize_path="D:\\Desktop\\";//初始化路径
public static function onWorkerStart($worker){
TcpConnection::$defaultMaxPackageSize = 2024000000;//2G
}
public static function onConnect($client_id){
}
public static function onWebSocketConnect($client_id, $data){
}
public static function onMessage($client_id, $message){
$message=json_decode($message,true);
$type=$message['type'];
switch ($type){
case "connect":
//来自1、2的消息连接绑定
Gateway::bindUid($client_id,$message['id']);
break;
case "open_path":
//来自1要打开路径的消息
Gateway::sendToUid(2,json_encode(["type"=>"open_path","data"=>$message['data']]));//发送给2
break;
case "return_open_path":
//来自2的路径及文件信息回显
$now_path=$message['data']['now_path'];
Cache::set("now_path",$now_path);
Gateway::sendToUid(1,json_encode(["type"=>"show_path","data"=>$message['data']]));//发送给1
break;
case "back":
//来自1发送的路径后退请求
$now_path=Cache::get("now_path");//获取当前路径
if ($now_path!=Events::$initialize_path){
//当前路径不等于初始化路径时可以退
Gateway::sendToUid(2,json_encode(["type"=>"open_path","data"=>dirname($now_path,1)]));
}
break;
/*
服务器为Linux请使用如下代码,以为Linux与Windows路径不同
case "back":
//来自1发送的路径后退请求
$now_path=Cache::get("now_path");//获取当前路径
$now_path = str_replace('\\','/',$now_path);
$initialize_path=Events::$initialize_path;
$initialize_path=str_replace('\\','/',$initialize_path);
if ($now_path!=$initialize_path){
//当前路径不等于初始化路径时可以退
$go_path=dirname($now_path,1);
$go_path=str_replace('/','\\',$go_path);
Gateway::sendToUid(2,json_encode(["type"=>"open_path","data"=>$go_path]));
}
break;
*/
case "download":
//来自1的下载请求
Gateway::sendToUid(2,json_encode(["type"=>"download","data"=> $message['data']]));//发送给2获取下载资源
break;
case "return_download":
//来自2返回的下载资源base64编码
Gateway::sendToUid(1,json_encode(["type"=>"return_download","data"=>$message['data']]));//发送给1
break;
}
}
public static function onClose($client_id){
}
}
server_connect核心代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<p onclick="back()"><后退</p>
<p id="now_path"></p>
<div id="box">
</div>
<script>
// 假设服务端ip为127.0.0.1
ws = new WebSocket("ws://127.0.0.1:7272");
var id=1;
var now_path;
ws.onopen = function() {
var arr={"type":"connect","id":id}
ws.send(JSON.stringify(arr))
};
ws.onmessage = function(e) {
var data= JSON.parse(e.data)
var type=data.type
switch (type){
case "show_path":
var d=data.data
now_path=d.now_path
$("#box").html("")//重置一下
$("#now_path").html("当前路径为:"+now_path)
if (d.dir){
for (var i=0;i<d.dir.length;i++){
var url=d.dir[i]
var encodeUrl=encodeURI(d.dir[i])//转义一下防止路径斜杠被转义
$("#box").append("<p onclick="+'open_path("'+encodeUrl+'")'+" style='color: red'>(文件夹)"+url+"</p>")
}
}
if (d.file){
for (var s=0;s<d.file.length;s++){
var url=d.file[s]
var encodeUrl=encodeURI(d.file[s])//转义一下防止路径斜杠被转义
$("#box").append("<p onclick="+'download("'+encodeUrl+'")'+" style='color: blue'>(文件)"+url+"</p>")
}
}
break;
case "return_download":
var base64=data.data.txt
var file_name=data.data.file_name
$.ajax({
url:'{:route("download")}',
type: "POST",
data:{txt:base64,file_name},
success: function(data) {
alert("下载完毕")
console.log(data)
window.open('{:route("get_file")}'+'?file_path='+data.file_path+'&file_name='+data.file_name,'_blank')
}
});//解析下载资源
break;
}
};
//打开指定路径
function open_path(data){
//console.log("方法在打印"+decodeURI(data))//解码
var arr={"type":"open_path","data":decodeURI(data)}
ws.send(JSON.stringify(arr))
}
//下载指定文件
function download(data){
//console.log("方法在打印"+decodeURI(data))//解码
console.log("正在下载:"+decodeURI(data))
console.log("大文件可能下载较慢请稍等")
var arr={"type":"download","data":decodeURI(data)}
ws.send(JSON.stringify(arr))
}
//后退
function back(){
var arr={"type":"back"}
ws.send(JSON.stringify(arr))
}
</script>
</body>
</html>
local_connect核心代码
<?php
ini_set('memory_limit', '2024M');
use Workerman\Worker;
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
TcpConnection::$defaultMaxSendBufferSize = 2024000000;
$worker = new Worker();
$worker->onWorkerStart = function($worker){
$con = new AsyncTcpConnection('ws://127.0.0.1:7272');
// websocket握手成功后
$con->onWebSocketConnect = function(AsyncTcpConnection $con) {
$con->send(json_encode(["type"=>"connect","id"=>2]));//发起连接请求
/*
*这里的路径需要与server_listen/plugin/webman/gateway/Events.php
* 里的$initialize_path相关联,$initialize_path比这里的路径多\\
*/
$con->send(json_encode(["type"=>"return_open_path","data"=>get_dir("D:\\Desktop")]));//发送初始化路径
};
// 当收到消息时
$con->onMessage = function(AsyncTcpConnection $con, $data) {
$data=json_decode($data,true);
$type=$data['type'];
switch ($type){
case "open_path":
//来自服务器转发的打开路径
$dir=$data['data'];
$con->send(json_encode(["type"=>"return_open_path","data"=>get_dir($dir)]));
break;
case "download":
//响应服务器的下载请求
$file_path=$data['data'];
$pattern = '/[^\\\\]*$/'; // 注意正则表达式中的双反斜杠,因为PHP需要转义反斜杠
if (preg_match($pattern, $file_path, $matches)) {
$file_name = $matches[0];
}
$txt=file_get_contents($file_path);
$txt=base64_encode($txt);
$con->send(json_encode(["type"=>"return_download","data"=>["txt"=>$txt,"file_name"=>$file_name]]));
break;
}
};
function get_dir($path) {
$list=[];
$list['dir']=[];
$list['file']=[];
$currentDir = opendir($path);
//opendir()返回一个目录句柄,失败返回false
while(($file = readdir($currentDir)) !== false) {
//readdir()返回打开目录句柄中的一个条目
$subDir = $path . DIRECTORY_SEPARATOR . $file;
//构建子目录路径
if(is_dir($subDir)) {
//如果是目录,进行递归
if (!preg_match("/\.\.?$/",$subDir)){
array_push($list['dir'],$subDir);//将文件目录中\.或\..文件不添加
}
} else {
//如果是文件,调用clasbackFun方法(参数:文件路径,文件名)
array_push($list['file'],$subDir);
}
}
$list['now_path']=$path. DIRECTORY_SEPARATOR;
return $list;
}
$con->connect();
};
Worker::runAll();