最终提交

This commit is contained in:
2026-05-21 13:24:05 +08:00
parent a1d1f19585
commit 1ab5940cbd
78 changed files with 313 additions and 4814 deletions

344
4G/源代码/lib/ftp.lua Normal file
View File

@@ -0,0 +1,344 @@
--- 模块功能FTP客户端
-- @module ftp
-- @author Dozingfiretruck
-- @license MIT
-- @copyright OpenLuat.com
-- @release 2020.12.08
require "socket"
require "utils"
module(..., package.seeall)
local ftp_client -- ftp命令连接socket对象
local ftp_data_client -- ftp数据连接socket对象
local data_client_ip, data_client_port -- ftp数据连接地址
--- FTP客户端关闭
function close()
ftp_client:send("QUIT\r\n")
ftp_client:close()
log.info("ftp", "ftp close")
end
--- FTP客户端命令
-- @string command 命令,例如"PWD","HELP","SYST"
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function command(command, timeout)
if not ftp_client:send(command .. "\r\n") then
close()
return '426', 'SOCKET_SEND_ERROR'
end
local r, n = ftp_client:recv(timeout)
if r == true and n:sub(1, 3) == "230" then
r, n = ftp_client:recv(timeout)
end
log.info("ftp_command", n)
if not r then
close()
return '503', 'SOCKET_RECV_TIMOUT'
else
return n:sub(1, 3), n:sub(4, #n)
end
end
--- 连接到PASV接口
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
local function pasv_connect(timeout)
---被动模式
if not ftp_client:send("PASV\r\n") then
close()
return '426', 'SOCKET_SEND_ERROR'
end
local r, n = ftp_client:recv(timeout)
if r == true and n:sub(1, 3) == "230" then
r, n = ftp_client:recv(timeout)
end
if not r then
close()
return '503', 'SOCKET_RECV_TIMOUT'
end
local h1, h2, h3, h4, p1, p2 =
n:match("(%d+),(%d+),(%d+),(%d+),(%d+),(%d+)")
data_client_ip, data_client_port =
h1 .. '.' .. h2 .. '.' .. h3 .. '.' .. h4,
string.format("%d", (p1 * 256 + p2))
log.info("ftp ip", data_client_ip, "port", data_client_port)
if not r then
close()
return '503', 'SOCKET_RECV_TIMOUT'
end
---创建ftp数据连接
ftp_data_client = socket.tcp()
log.info("ftp ip", data_client_ip, "port", data_client_port)
if not ftp_data_client:connect(data_client_ip, data_client_port) then
close()
ftp_data_client:close()
return '502', 'SOCKET_CONN_ERROR'
end
return '200', 'ftp pasv success'
end
--- FTP客户端登录
-- @string ftp_mode FTP模式"PASV"or"PORT",默认PASV:被动模式,PORT:主动模式(暂时仅支持被动模式)
-- @string host ip地址
-- @string port 端口,默认21
-- @string username 用户名
-- @string password 密码
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @bool ssl 可选参数默认为nilssl是否为ssl连接true表示是其余表示否
-- @table cert 可选参数默认为nilcertssl连接需要的证书配置只有ssl参数为true时才参数才有意义cert格式如下
-- {
-- caCert = "ca.crt", --CA证书文件(Base64编码 X.509格式),如果存在此参数,则表示客户端会对服务器的证书进行校验;不存在则不校验
-- clientCert = "client.crt", --客户端证书文件(Base64编码 X.509格式),服务器对客户端的证书进行校验时会用到此参数
-- clientKey = "client.key", --客户端私钥文件(Base64编码 X.509格式)
-- clientPassword = "123456", --客户端证书文件密码[可选]
-- }
-- @return string,string,返回 response_code, response_message
function login(ftp_mode, host, port, username, password, timeout, ssl, cert)
if ftp_mode ~= "PASV" then
log.error("暂不支持主动模式 ")
return '-1', 'ftp ftp_mode error'
end
while not socket.isReady() do sys.wait(1000) end
ftp_client = socket.tcp(ssl, cert)
if not ftp_client:connect(host, port or 21) then
close()
return '502', 'SOCKET_CONN_ERROR'
end
local r, n = ftp_client:recv(timeout)
if not r then
close()
return '503', 'SOCKET_RECV_TIMOUT'
end
if not ftp_client:send("USER " .. username .. "\r\n") then
close()
return '426', 'SOCKET_SEND_ERROR'
end
r = ftp_client:recv(timeout)
if not r then
close()
return '503', 'SOCKET_RECV_TIMOUT'
end
-- 密码
if not ftp_client:send("PASS " .. password .. "\r\n") then
close()
return '426', 'SOCKET_SEND_ERROR'
end
r, n = ftp_client:recv(timeout)
if not r then
close()
return '503', 'SOCKET_RECV_TIMOUT'
end
if n:sub(1, 3) == '230' then
log.info("ftp", n)
elseif n:sub(1, 3) == '530' then
log.error("ftp Password error ", n)
close()
return '530', n
end
log.info("ftp login success")
return '200', 'ftp login success'
end
--- FTP客户端文件上传
-- @string remote_file 远程文件名
-- @string local_file 本地文件名
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function upload(remote_file, local_file, timeout)
local com, msg = pasv_connect()
if com ~= "200" then return com, msg end
-- 文件上传
if not ftp_client:send("APPE " .. remote_file .. "\r\n") then
close()
ftp_data_client:close()
return '426', 'SOCKET_SEND_ERROR'
end
local r, n = ftp_client:recv(timeout)
log.info("ftp upload", n)
if not r then
close()
ftp_data_client:close()
return '503', 'SOCKET_RECV_TIMOUT'
end
if n:sub(1, 3) == '553' then
log.error("ftp STOR error ", n)
close()
ftp_data_client:close()
return '553', n
end
local file = io.open(local_file, "r")
if file then
local file_size = io.fileSize(local_file)
local file_cnt = 0
log.info("ftp", "local_file_size", file_size)
repeat
log.info("ftp", "file_cnt", file_cnt, "file_size - file_cnt",
file_size - file_cnt)
local temp_data = io.readStream(local_file, file_cnt, 11200)
if not ftp_data_client:send(temp_data) then
close()
ftp_data_client:close()
return '426', 'SOCKET_SEND_ERROR'
end
file_cnt = file_cnt + 11200
until (file_size - file_cnt < 11200)
local temp_data = io.readStream(local_file, file_cnt,
file_size - file_cnt)
if not ftp_data_client:send(temp_data) then
close()
ftp_data_client:close()
return '426', 'SOCKET_SEND_ERROR'
end
file:close()
ftp_data_client:close()
return '200', 'ftp success'
else
log.info("文件打开失败")
ftp_data_client:close()
return '503', 'file open error'
end
end
--- FTP客户端文件下载
-- @string remote_file 远程文件名
-- @string local_file 本地文件名
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function download(remote_file, local_file, timeout)
local com, msg = pasv_connect()
if com ~= "200" then return com, msg end
-- 文件大小
if not ftp_client:send("SIZE " .. remote_file .. "\r\n") then
close()
ftp_data_client:close()
return '426', 'SOCKET_SEND_ERROR'
end
local r, n = ftp_client:recv(timeout)
if not r then
close()
ftp_data_client:close()
return '503', 'SOCKET_RECV_TIMOUT'
end
if n:sub(1, 3) == '213' then
log.info("ftp filename size", n:sub(5, #n))
filename_size = tonumber(n:sub(5, #n))
elseif n:sub(1, 3) == '550' then
log.error("ftp filename error ", n)
return
end
-- 文件下载
if not ftp_client:send("RETR " .. remote_file .. "\r\n") then
close()
ftp_data_client:close()
return '426', 'SOCKET_SEND_ERROR'
end
local r, n = ftp_client:recv(timeout)
log.info("ftp download", n)
if not r then
close()
ftp_data_client:close()
return '503', 'SOCKET_RECV_TIMOUT'
end
if io.exists(local_file) then
os.remove(local_file)
log.info("删除文件", local_file)
end
local file = io.open(local_file, "ab")
if file then
while true do
local r, file_data = ftp_data_client:recv(timeout)
if r then
if file_data:find("226 Successfully transferred ") == nil and
file_data:find("226 Transfer OK") == nil then
file:write(file_data)
end
else
break
end
end
file:close()
ftp_data_client:close()
return '200', 'ftp success'
else
log.info("文件打开失败")
ftp_data_client:close()
return '503', 'file open error'
end
end
--- 设置FTP传输类型 A:ascii I:Binary
-- @string mode A:ascii I:Binary
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function checktype(mode, timeout) return command("TYPE " .. mode, timeout) end
--- 显示当前工作目录
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function pwd(timeout) return command("PWD ", timeout) end
--- 更改工作目录
-- @string path 工作目录
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function cwd(path, timeout) return command("CWD " .. path, timeout) end
--- 回到上级目录
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function cdup(timeout) return command("CDUP", timeout) end
--- 创建目录
-- @string path 目录
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function mkd(path, timeout) return command("MKD " .. path, timeout) end
--- 列出目录列表或文件信息
-- @string file_irectory 目录或文件
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function list(file_irectory, timeout)
local com, msg = pasv_connect()
if com ~= "200" then return com, msg end
local r, n = command("LIST " .. file_irectory, timeout)
if r == "503" then return r, n end
r, n = ftp_client:recv(timeout)
log.info("ftp", r, n)
if not r then
close()
ftp_data_client:close()
return '503', 'SOCKET_RECV_TIMOUT'
end
local data = ""
while true do
local r, n = ftp_data_client:recv(timeout)
if r then
data = data .. n
else
break
end
end
ftp_data_client:close()
return r, data
end
--- 删除目录
-- @string file_irectory 路径目录
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function deletefolder(file_irectory, timeout)
return command("RMD " .. file_irectory, timeout)
end
--- 删除文件
-- @string file_irectory 路径文件(相对/绝对)
-- @number[opt=0] timeout 接收超时时间,单位毫秒
-- @return string,string,返回 response_code, response_message
function deletefile(file_irectory, timeout)
return command("DELE " .. file_irectory, timeout)
end