添加SFTP文件上传功能

This commit is contained in:
OldCat 2020-12-18 14:35:58 +08:00
parent 7deb10638d
commit f438fe919d
12 changed files with 684 additions and 39 deletions

View File

@ -5,6 +5,8 @@ go版本多用户webssh管理工具
ssh2ws部分代码修改自https://github.com/hequan2017/go-webssh
2020/12/17 新增WEB_SFTP功能拖动文件到终端窗口里即可上传
服务端不保存用户明文密码,且不保存解密秘钥,如需对其他用户开放,请不要修改此部分代码,以免造成不必要的损失!
@ -14,10 +16,11 @@ Gin + gorm
## 更新日志
2020/12/14 修复无操作自动断开、修复网络延迟造成的js加载延迟问题
2020/12/16 前端新增文件/文件夹拖动到Terminal的自动解析功能SFTP需要修改layer弹出窗口逻辑增加回车提交事件
2020/12/17 增加在线sftp文件上传功能 *
## 开发计划
✔ ssh功能
× 服务器文件管理
✔ sftp文件上传功能
## 在线演示
[点击进入SSH云管理平台](https://www.do18.cn)

View File

@ -194,7 +194,7 @@ func (ssConn *SshConn) SessionWait(quitChan chan bool) {
// log.Println("ssh session wait failed")
// setQuit(quitChan)
//}
timer := time.NewTicker(time.Second * 3)
timer := time.NewTicker(time.Second * 5)
defer timer.Stop()
for {
select {

181
common/sftp_clients/sftp.go Normal file
View File

@ -0,0 +1,181 @@
package sftp_clients
import (
"encoding/base64"
"encoding/json"
"github.com/gorilla/websocket"
"github.com/pkg/sftp"
"log"
"path"
"sync"
"time"
)
const (
getpwd = "getpwd"
upload = "upload"
)
type sftp_req struct {
Type string `json:"type"`
FilePath string `json:"filepath"`
FileName string `json:"filename"`
FileData string `json:"filedata"`
}
type sftp_resp struct {
Code int `json:"code"`
Type string `json:"type"`
Msg string `json:"msg"`
Data string `json:"data"`
}
type MyClient struct {
Uid uint
Sftp *sftp.Client
}
type clients struct {
sync.RWMutex
C map[string]*MyClient
}
var Client clients
func init() {
Client = clients{C: make(map[string]*MyClient, 1000)}
}
func (c *MyClient) ReceiveWsMsg(wsConn *websocket.Conn, exitCh chan bool) {
defer setQuit(exitCh)
go c.SessionWait(wsConn, exitCh)
for {
select {
case <-exitCh:
return
default:
_, wsData, err := wsConn.ReadMessage()
if err != nil {
log.Println(err.Error())
//logrus.WithError(err).Error("reading webSocket message failed")
return
}
//unmashal bytes into struct
msgObj := sftp_req{}
if err := json.Unmarshal(wsData, &msgObj); err != nil {
log.Println("unmarshal websocket message failed:", string(wsData))
continue
}
msgresp := sftp_resp{}
switch msgObj.Type {
case getpwd:
msgresp.Code = 200
msgresp.Type = "pwd"
path, err := c.Sftp.Getwd()
if err != nil {
msgresp.Code = 404
msgresp.Msg = "服务器Path获取失败"
msgresp.Data = err.Error()
log.Println("sftp getpwd err:", err.Error())
}
msgresp.Data = path
msg, _ := json.Marshal(msgresp)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp client getpwd err:", err.Error())
return
}
case upload:
msgresp.Code = 200
msgresp.Type = "upload"
io_data, err := base64.StdEncoding.DecodeString(msgObj.FileData)
if err != nil {
msgresp.Code = 401
msgresp.Msg = "文件解析失败"
msgresp.Data = err.Error()
log.Println("sftp base64decode err:", err.Error())
msg, _ := json.Marshal(msgresp)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp base64decode send err:", err.Error())
return
}
}
if err := c.Sftp.MkdirAll(msgObj.FilePath); err != nil {
msgresp.Code = 402
msgresp.Msg = "服务器创建目录失败"
msgresp.Data = err.Error()
log.Println("sftp mkdir err:", err.Error())
msg, _ := json.Marshal(msgresp)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp mkdir send err:", err.Error())
return
}
continue
}
file, err := c.Sftp.Create(path.Join(msgObj.FilePath, msgObj.FileName))
if err != nil {
msgresp.Code = 403
msgresp.Msg = "服务器文件创建失败"
msgresp.Data = err.Error()
log.Println("sftp create file err:", err.Error())
msg, _ := json.Marshal(msgresp)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp create file send err:", err.Error())
return
}
continue
}
defer file.Close()
if _, err := file.Write(io_data); err != nil {
msgresp.Code = 405
msgresp.Msg = "服务器文件写入失败"
msgresp.Data = err.Error()
log.Println("sftp write file err:", err.Error())
msg, _ := json.Marshal(msgresp)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp write file send err:", err.Error())
return
}
continue
}
file.Close()
filepath := path.Join(msgObj.FilePath, msgObj.FileName)
msgresp.Code = 200
msgresp.Msg = "OK"
msgresp.Data = filepath
//log.Println("file write ok")
msg, _ := json.Marshal(msgresp)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp write file send err:", err.Error())
return
}
}
}
}
}
func (c *MyClient) SessionWait(wsConn *websocket.Conn, quitChan chan bool) {
timer := time.NewTicker(time.Second * 30)
defer timer.Stop()
defer setQuit(quitChan)
for {
select {
case <-timer.C:
{
if err := wsConn.WriteMessage(websocket.TextMessage, []byte("pong")); err != nil {
log.Println("sftp pong send err :", err.Error())
return
}
}
case <-quitChan:
return
}
}
}
func setQuit(ch chan bool) {
ch <- true
}

114
controller/stfp.go Normal file
View File

@ -0,0 +1,114 @@
package controller
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
"log"
"ssh_manage/common"
"ssh_manage/common/core"
"ssh_manage/common/sftp_clients"
"ssh_manage/errcode"
"ssh_manage/model/Apiform"
)
type sftp_req struct {
Type string `json:"type"`
Token string `json:"token"`
}
type sftp_resp struct {
Code int `json:"code"`
Type string `json:"type"`
Msg string `json:"msg"`
Data string `json:"data"`
}
func Sftp_ssh(c *gin.Context) {
wsConn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
if core.HandleError(c, err) {
return
}
defer wsConn.Close()
var auth Apiform.WsAuth
if c.ShouldBindUri(&auth) != nil {
wsConn.WriteMessage(websocket.TextMessage, []byte("参数错误\r\n"))
wsConn.Close()
return
}
for {
_, wsData, err := wsConn.ReadMessage()
if err != nil {
log.Println(err.Error())
wsConn.Close()
//logrus.WithError(err).Error("reading webSocket message failed")
return
}
//unmashal bytes into struct
msgObj := sftp_req{}
if err := json.Unmarshal(wsData, &msgObj); err != nil {
log.Println("Auth : unmarshal websocket message failed:", string(wsData))
continue
}
resp_msg := sftp_resp{}
token := msgObj.Token
claims, err := common.ParseToken(token)
valid := claims.Valid()
if valid != nil || err != nil {
resp_msg.Code = errcode.S_auth_fmt_err
resp_msg.Msg = "身份令牌校验不通过"
resp_msg.Data = err.Error()
msg, _ := json.Marshal(resp_msg)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp token fmt err:", err)
}
wsConn.Close()
return
}
if claims.Userid != sftp_clients.Client.C[auth.Sid].Uid { //身份与缓存不符合
resp_msg.Code = errcode.S_auth_fmt_err
resp_msg.Msg = "用户权限不通过"
resp_msg.Data = err.Error()
msg, _ := json.Marshal(resp_msg)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp server_user err:", err)
}
wsConn.Close()
return
}
path, err := sftp_clients.Client.C[auth.Sid].Sftp.Getwd()
if err != nil {
resp_msg.Code = errcode.S_send_err
resp_msg.Type = "connect"
resp_msg.Msg = "SFTP连接失败"
msg, _ := json.Marshal(resp_msg)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp connect err:", err)
}
return
}
resp_msg.Code = 200
resp_msg.Type = "connect"
resp_msg.Msg = "连接成功"
resp_msg.Data = path
msg, _ := json.Marshal(resp_msg)
if err := wsConn.WriteMessage(websocket.TextMessage, msg); err != nil {
log.Println("sftp return err:", err)
return
}
break
//break
}
quitChan := make(chan bool, 2)
go sftp_clients.Client.C[auth.Sid].ReceiveWsMsg(wsConn, quitChan)
<-quitChan //任意协程退出则结束
fmt.Println("Sftp Exit")
log.Println("sftp websocket finished")
}

View File

@ -10,6 +10,7 @@ import (
"net/http"
"ssh_manage/common"
"ssh_manage/common/core"
"ssh_manage/common/sftp_clients"
"ssh_manage/database"
"ssh_manage/model/Apiform"
"strconv"
@ -114,6 +115,14 @@ func WsSsh(c *gin.Context) {
if core.WshandleError(wsConn, err) {
return
}
sftp_clients.Client.Lock()
sftp_clients.Client.C[auth.Sid] = &sftp_clients.MyClient{ser_info.BindUser, ssConn.SftpClient}
sftp_clients.Client.Unlock()
defer func() {
sftp_clients.Client.Lock()
delete(sftp_clients.Client.C, auth.Sid) //释放SFTP客户端
sftp_clients.Client.Unlock()
}()
defer ssConn.Close()
quitChan := make(chan bool, 3)

View File

@ -55,6 +55,7 @@ func main() {
api.POST("/login", controller.Login)
api.POST("/send", controller.Send)
api.GET("/term/:sid", controller.WsSsh)
api.GET("/sftp/:sid", controller.Sftp_ssh)
api.Use(middleware.Auth()).GET("/userinfo", controller.Info)
api.Use(middleware.Auth()).POST("/nickname", controller.UpdataNick)
api.Use(middleware.Auth()).POST("/addser", controller.Addser)

View File

@ -65,7 +65,7 @@ getinfo = function () {
}
, parseData: function (res) { //将原始数据解析成 table 组件所规定的数据
if (res.token) {
window.localStorage.setItem("token", result.token) //更新token
window.localStorage.setItem("token", res.token) //更新token
}
if (res.code == 301 || res.code == 302) { //Token校验失败或过期
layer.msg(res.msg, function () {

View File

@ -1,6 +1,29 @@
let dropbox = document.getElementById("terms"); //要监听拖动上传的节点
let fileDrop = {
startTime: 0,
endTime: 0,
uploadLength: 0, //上传数量
//splitSize: 1024 * 1024 * 2, //文件上传分片大小
filesList: [], // 文件列表数组
errorLength: 0, //上传失败文件数量
isUpload: true, //上传状态,是否可以上传
uploadSuspend: false, //上传暂停参数
isUploadNumber: 150,//限制单次上传数量
uploadAllSize: 0, // 上传文件总大小
uploadedSize: 0, // 已上传文件大小
topUploadedSize: 0, // 上一次文件上传大小
uploadExpectTime: 0, // 预计上传时间
//initTimer:0, // 初始化计时
speedInterval: null, //平局速度定时器
timerSpeed: 0, //速度
uploading: false,
cancel: false,
done: false,
}
function file_init() {
fileDrop = {
startTime: 0,
endTime: 0,
uploadLength: 0, //上传数量
@ -20,6 +43,7 @@ let fileDrop = {
uploading: false,
cancel: false,
}
}
dropbox.addEventListener("dragleave", function (e) {
//e.stopPropagation();
@ -39,8 +63,9 @@ dropbox.addEventListener("dragover", function (e) {
dropbox.addEventListener("drop", changes, false);
function changes(e) {
if(!is_login){
if (!sftp_ready) {
layer.msg("请等待服务器连接!")
return false
}
e.preventDefault();
let items = e.dataTransfer.items, time, num = 0
@ -67,10 +92,11 @@ function changes(e) {
if (typeof (filesAndDirs[i].getFilesAndDirectories) == 'function') {
update_sync(filesAndDirs[i])
} else {
if (num > 100) {
if (num > fileDrop.isUploadNumber) {
//fileDrop.isUpload = false;
layer.msg(' ' + fileDrop.isUploadNumber + '份,无法上传,请压缩后上传!。', {icon: 2, area: '405px'});
//clearTimeout(time);
file_init()
return false;
}
fileDrop.filesList.push({
@ -96,28 +122,32 @@ function changes(e) {
layer.load(1, {
shade: [0.1, '#fff'] //0.1透明度的白色背景
});
getpwd(getpwd_callback) //采用回调函数的方式检查SFTP服务是否可用
}
function getpwd_callback(msg) {
server_pwd = msg.data
setTimeout(function () {
layer.closeAll('loading')
open_upload_window()
},3000)
}, 1500)
}
function open_upload_window() {
//console.log(fileDrop.filesList[0])
let template = `
<table class="layui-table" lay-even="" lay-skin="row" id="file_upload" style="table-layout: fixed;padding-top: 0">
<colgroup>
<col width="250">
<col width="150">
<col width="150">
<col width="35%">
<col width="35%">
<col width="30%">
<col>
</colgroup>
<thead>
<tr>
<th>文件路径</th>
<th>文件大小</th>
<th>状态</th>
<th>文件路径` + fileDrop.uploadLength + `个文件</th>
<th>文件大小` + to_size(fileDrop.uploadAllSize) + `</th>
<th>文件状态</th>
</tr>
</thead>
<tbody align="center">
@ -130,8 +160,8 @@ function open_upload_window() {
closeBtn: 1,
maxmin: true,
area: ['550px', '455px'],
btn: ['开始上传', '取消上传'],
title: '上传文件',
btn: [fileDrop.uploadSuspend ? "继续上传" : "开始上传", '取消上传'],
title: '上传文件到:' + session_path,
skin: 'file_dir_uploads',
shade: 0.4,
shadeClose: false,
@ -140,13 +170,139 @@ function open_upload_window() {
for (let i = 0; i < fileDrop.filesList.length; i++) {
$("#file_upload tbody").append(create_row(i, fileDrop.filesList[i]));
}
},
yes: function (index) {
if (fileDrop.filesList.length <= 0) {
layer.msg("请选择要上传的文件!", {icon: 0})
file_init()
layer.close(index)
return false
}
if (fileDrop.uploading && fileDrop.uploadSuspend == false) {
return false
}
//console.log("开始上传")
$('.layui-layer-btn0').css({
'cursor': 'no-drop',
'background': '#4e8ccc'
}).attr('data-upload', 'true').text('上传中');
fileDrop.uploadSuspend = false
fileDrop.uploading = true
start_upload()
},
btn2: function (index) {
if(fileDrop.done){
layer.close(index);
file_init()
return false
}
if (fileDrop.uploading) {
layer.confirm('是否取消上传当前列表的文件,若取消上传,已上传的文件,需用户手动删除,是否取消上传?', {
title: '取消上传文件',
icon: 0,
btn: ['取消上传', '继续上传']
}, function (indexs) {
layer.close(index);
layer.close(indexs);
file_init()
}, function () {
fileDrop.uploadSuspend = true //开启暂停标识,用于继续上传功能
open_upload_window()
});
//return false;
} else {
layer.close(index);
file_init()
}
},
cancel: function (index) {
if(fileDrop.done){
layer.close(index);
file_init()
return false
}
if (fileDrop.uploading) {
layer.confirm('是否取消上传当前列表的文件,若取消上传,已上传的文件,需用户手动删除,是否取消上传?', {
title: '取消上传文件',
icon: 0,
btn: ['取消上传', '继续上传']
}, function (indexs) {
layer.close(index);
layer.close(indexs);
file_init()
}, function () {
fileDrop.uploadSuspend = true //开启暂停标识,用于继续上传功能
open_upload_window()
});
//return false;
} else {
layer.close(index);
file_init()
}
},
});
}
function start_upload(file_index, status, msg) {
file_index = file_index == null ? 0 : file_index
if (status) { //这个参数只在上传回调中传递用于更新文件上传结果如果这个参数存在表示file_index已经走完上传流程故file_index需自增1代表开始下一个文件的上传
// if(msg){
// upload_view(file_index,msg)
// }else{
// upload_view(file_index,status)
// }
fileDrop.filesList[file_index].upload = status //更新文件列表中的状态
upload_view(file_index, msg == undefined ? status : msg)
file_index++
}
let reader = new FileReader(); //这个可以选择函数外声明,可避免重复创建
if (reader == undefined) {
file_init()
layer.msg("您的浏览器暂不支持在线上传文件!")
return false
}
reader.onload = function () {
let fileData = _arrayBufferToBase64(this.result);
fileDrop.filesList[file_index].upload = 1 //修改状态为上传中
upload_view(file_index, 1) //修改视图内容
sendFile(fileDrop.filesList[file_index], fileData, file_index, start_upload)
//console.log(fileData);
}
if (file_index < fileDrop.filesList.length) { //采用回调方式上传,进度更可控
if (fileDrop.filesList[file_index].upload < 2) { //可继续上传未上传的文件
let f_id = "#file_" + file_index + " td";
$(f_id)[2].innerHTML = "<font color='#808080'>解析中</font>";
reader.readAsArrayBuffer(fileDrop.filesList[file_index].file)
}
} else {
layer.msg("上传完成")
$('.layui-layer-btn0').text('上传完成');
$('.layui-layer-btn1').text('关闭窗口');
fileDrop.done = true
}
// for (let i = 0; i < fileDrop.filesList.length; i++) {
// reader.readAsArrayBuffer(fileDrop.filesList[i].file);
// }
}
function upload_view(file_index, status) {
let f_id = "#file_" + file_index + " td";
$(f_id)[2].innerHTML = getstatu(status);
}
function _arrayBufferToBase64(buffer) {
let binary = '';
let bytes = new Uint8Array(buffer);
let len = bytes.byteLength;
for (var i = 0; i < len; i++) {
binary += String.fromCharCode(bytes[i]);
}
return window.btoa(binary);
}
function create_row(index, file) {
console.log(file)
return "<tr id='" + index + "'><td title='" + file.local + "' style=\"white-space:nowrap;overflow:hidden;text-overflow: ellipsis;\">" + file.local + "</td> <td>" + file.size + "</td> <td>" + getstatu(file.upload) + "</td></tr>"
//console.log(file)
return "<tr id='file_" + index + "'><td title='" + file.local + "' style=\"white-space:nowrap;overflow:hidden;text-overflow: ellipsis;\">" + file.local + "</td> <td>" + file.size + "</td> <td>" + getstatu(file.upload) + "</td></tr>"
}
function getstatu(statu) {
@ -158,8 +314,10 @@ function getstatu(statu) {
return "<font color='black'>未上传</font>"
} else if (statu == 1) {
return "<font color='#808080'>上传中</font>"
} else {
} else if (statu == 2) {
return "<font color='green'>已上传</font>"
} else {
return "<font color='red'>" + statu + "</font>"
}
}
}

109
static/js/sftp.js Normal file
View File

@ -0,0 +1,109 @@
var sftp = null;
var sftp_ready = false;
var server_pwd = "";
var session_path = "";
var callback_success = undefined;
//var callback_error = undefined;
var upload_file_index = 0;
let sendmsg = {
type: "",
filepath: "",
filename: "",
filedata: "",
}
let msg_type = {
connect: "connect",
getpwd: "getpwd",
upload: "upload",
}
let file_status = {
success: 2, //上传成功
error: -1, //上传失败
}
create_sftp = function () {
sftp = new WebSocket(ws_p + '://' + window.location.host + '/v1/sftp/' + GetQueryString("sid"));
sftp.onopen = function () {
sftp.send(JSON.stringify(auth)); //验证权限
sftp.onmessage = default_msg;
sftp.onerror = function (e) {
sftp_ready = false
console.log(e);
layer.msg("SFTP连接出错")
};
sftp.onclose = function (e) {
sftp_ready = false
console.log(e);
layer.msg("SFTP连接断开")
};
};
}
default_msg = function (msg) {
if (isJSON(msg.data)) {
msg = JSON.parse(msg.data)
if (msg.type == msg_type.connect && msg.code == 200) {
sftp_ready = true
server_pwd = msg.data //初始化工作路径
} else if (msg.type == msg_type.upload) {
if (msg.code == 200) {
callback_success(upload_file_index, file_status.success)
} else {
//layer.msg("文件上传失败:" + msg.msg) //可以回调修改状态文字内容,这里为了图方便,暂时不写了;(觉得不妥,又写上了)
callback_success(upload_file_index, file_status.error,msg.msg) //上传失败正常回调,开始处理下一个
}
}
}
}
getpwd = function (callback) {
sendmsg.type = msg_type.getpwd
sftp.onmessage = function (msg) {
if (isJSON(msg.data)) {
msg = JSON.parse(msg.data)
if (msg.code == 200) {
if (session_path == "") {
session_path = server_pwd
}
callback(msg)
} else {
layer.msg(msg.msg)
}
sftp.onmessage = default_msg
}
}
sftp.send(JSON.stringify(sendmsg))
}
sendFile = function (fileinfo, fileData, file_index, callback) {
sendmsg.type = msg_type.upload
sendmsg.filepath = session_path + fileinfo.path
sendmsg.filename = fileinfo.name
sendmsg.filedata = fileData
callback_success = callback
upload_file_index = file_index
sftp.send(JSON.stringify(sendmsg))
}
isJSON = function (str) {
if (typeof str == 'string') {
try {
var obj = JSON.parse(str);
if (typeof obj == 'object' && obj) {
return true;
} else {
return false;
}
} catch (e) {
//console.log('error' + str + '!!!' + e);
return false;
}
}
//console.log('It is not a string!')
}

View File

@ -1,4 +1,23 @@
var is_login = false;
// var is_login = { watchValue:false };
// var lastTimeValue=is_login.watchValue;
// Object.defineProperty(is_login, 'watchValue', {
// get: function() {
// console.log('get' + watchValue);
// return watchValue;
// },
// set: function(value) {
// watchValue = value;
// if(lastTimeValue!=watchValue){
// lastTimeValue=watchValue;
// console.log('value changed!! set: ' + watchValue);
// if(watchValue == true){
// create_sftp()
// }
// }
// }
// });
var is_login = false
const protocol = document.location.protocol.split(':')[0];
var ws_p = "ws";
if (protocol == "https") {
@ -15,7 +34,16 @@ const auth = {
type: "auth",
token: token,
}
function completeLoading() {
console.log("Loading Success")
if (document.readyState == "complete") {
layer.close(index);
}
}
const socket = new WebSocket(ws_p + '://' + window.location.host + '/v1/term/' + GetQueryString("sid"));
const term = new Terminal({cols: 180, rows: 50, screenKeys: true, cursorBlink: true, cursorStyle: "block"});
term.open(document.getElementById('terms'));
window.onresize = function () {
@ -46,19 +74,50 @@ socket.onopen = function () {
socket.onmessage = function (msg) {
if (!is_login) {
term.clear()
create_sftp()
is_login = true
}
term.write(msg.data);
update_path(msg.data)
};
socket.onerror = function (e) {
is_login = false
layer.msg("链接出错:" + JSON.stringify(e))
console.log(e);
};
socket.onclose = function (e) {
is_login = false
console.log(e);
term.write("连接已断开:" + e.reason + "\r\n");
layer.msg("链接断开:" + JSON.stringify(e))
term.write("连接已断开" + "\r\n");
//term.destroy();
};
};
function update_path(str) { //判断是否有效路径后更新当前所在路径用于SFTP功能
//console.log(str)
let splice = String.fromCharCode(7)
let start = str.indexOf(":")
let end = str.indexOf(splice)
if (start >= 0 && end >= 0) {
let path = trimStr(str.substring(start + 1, end))
let verify = path.indexOf(" ")
if(verify == -1){
//console.log(path)
if(path.substr(0,1) == "~" || path.substr(0,1) == "/"){
if(path.substr(0,1) == "~"){ //替换~为用户目录
path = server_pwd + path.substr(1)
}
//console.log(path)
if(session_path != path){
session_path = path
}
}
}
}
}
function trimStr(str){
return str.replace(/(^\s*)|(\s*$)/g,"");
}

View File

@ -44,7 +44,7 @@
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="/static/assets/layui.all.js?v=1"></script>
<script src="/static/js/net.js?v=1.3"></script>
<script src="/static/js/console.js?v=1.99.1"></script>
<script src="/static/js/console.js?v=1.99.2"></script>
<script>
var index = layer.load(0, {shade: [0.1,'#fff']});
document.onreadystatechange = completeLoading;
@ -53,6 +53,16 @@
console.log("Loading Success")
if (document.readyState == "complete") {
layer.close(index);
if(window.localStorage.getItem("upload_info") == undefined){
layer.alert('更新提示', {
skin: 'layui-layer-lan'
,closeBtn: 0
,anim: 4 //动画类型
,title: "更新提示"
,content: "在线SFTP上传功能已实现可拖动本地文件或文件夹到终端窗口内进行上传操作"
});
}
window.localStorage.setItem("upload_info","read")
}
}
</script>

View File

@ -38,7 +38,8 @@
<script src="/static/xterm/fit.min.js"></script>
<script src="/static/xterm/search.min.js"></script>
<script src="/static/js/net.js?v=1.9"></script>
<script src="/static/xterm/main.js?v=0.62"></script>
<script src="/static/js/sftp.js?v=0.17"></script>
<script src="/static/xterm/main.js?v=0.87"></script>
<script src="/static/js/polyfill.js"></script>
<script src="/static/js/fileupload.js?v=0.30"></script>
<script src="/static/js/fileupload.js?v=0.75"></script>
</html>