//ftp 文件传输协议 import inet; import fsys; namespace inet class ftp { ctor(server,user,password,port = 21,pasv = true,agent = "Mozilla/4.0", proxy,proxyBypass="",flags=0 ){ port = tonumber(port); if( #proxy ){ this.session = ::InternetOpen(agent,0x3/*_INTERNET_OPEN_TYPE_PROXY*/,proxy,proxyBypass,flags); } else{ this.session = ::InternetOpen(agent,(!proxy)? 0x1/*_INTERNET_OPEN_TYPE_DIRECT*/:0x0/*_INTERNET_OPEN_TYPE_PRECONFIG*/,null,null,flags); } if(!this.session) error("inet.ftp初始化失败",2) //连接 this.connect = ::InternetConnect(this.session ,server ,port ,user ,password , 1 , pasv?0x8000000:0 //是否被动模式 , 0); if( not this.connect ) return null,"连接FTP服务器失败"; ..table.gc(this,"close"); }; close = function(){ if(this.connect) ::InternetCloseHandle(this.connect); if(this.session) ::InternetCloseHandle(this.session); }; command = function(cmdStr,flags=2/*_INTERNET_FLAG_TRANSFER_BINARY*/){ if(type(cmdStr)!="string"){ error("参数 @1 必须是字符串",2) } var cmd = ..string.match(cmdStr,"\s*(\a+)"); if(cmd){ cmd = ..string.upper(cmd); if(_DATA_CONNECTION_COMMANDS[cmd]){ var ok,hFile = FtpCommand(this.connect,true,flags,cmd ); if(ok){ return file(this.connect,hFile); } return null; } } var ok = FtpCommand(this.connect,false,flags,cmd ); if(ok){ return ..inet.lastResponse() || ""; } }; upload=function(path,remotePath,flags=0){ if( !..io.exist(path) ){ return false,"上传出错:本地文件不存在",path } remotePath = ..string.replace(remotePath,"@@\","/") return FtpPutFile(this.connect,..io.fullpath(path),remotePath,flags,0) }; download=function(local,remote,failIfExists,flags=0){ return FtpGetFile(this.connect,remote,..io.fullpath(local),failIfExists,0x80/*_FILE_ATTRIBUTE_NORMAL*/,flags,0) }; delete = function(path){ path = ..string.replace(path,"\\","/"); FtpDeleteFile(this.connect,path); } deleteDir = function(path){ return FtpRemoveDirectory(this.connect,canonicalizeDirectory(path) ); }; createDir = function(path){ if(!path)error("目录不能为空",2); path = canonicalizeDirectory(path); var parent,last = ..string.match(path,"(.+)/([^/]+/)") if( parent && last ){ owner.createDir(parent) }; return FtpCreateDirectory(this.connect,canonicalizeDirectory(path)); }; createParentDir = function(path){ if(!path)error("路径参数不能为空",2); var pdir = getParentDir(path); if( pdir ) return this.createDir( getParentDir(path) ); }; rename = function(path,new){ return FtpRenameFile(this.connect,..string.replace(path,"\\","/"),..string.replace(new,"\\","/") ); }; getCurDir = function(){ var ok,path,len = FtpGetCurrentDirectory(this.connect,0x208/*_MAX_PATH_U16*/,0x104/*_MAX_PATH*/); if(ok)return canonicalizeDirectory(path); }; setCurDir = function(path){ if(!path)error("目录不能为空",2); return FtpSetCurrentDirectory(this.connect,canonicalizeDirectory(path) ) }; eachDir = function(dir,p="*.*"){ var oldCurDir = this.getCurDir(); dir = canonicalizeDirectory(dir); FtpSetCurrentDirectory(this.connect,dir); dir = this.getCurDir(); var hfile; var findData = ..fsys.WIN32_FIND_DATA() var freeItor = function(){ if(hfile){ ::InternetCloseHandle(hfile); hfile = null; FtpSetCurrentDirectory(this.connect,oldCurDir ); } } var gcFile; return function(){ if(hfile){ if(!InternetFindNextFile(gcFile,findData)) return; } else{ hfile = FtpFindFirstFile(this.connect,p,findData,0,0); if(!hfile) return; gcFile = ..gcdata( _gc = freeItor ;_topointer = hfile ) } var fileName =..string.str( findData.cFileName ); if( findData.dwFileAttributes & 0x10/*_FILE_ATTRIBUTE_DIRECTORY*/ ) return fileName/*dirName*/, ,findData,dir else return dir,fileName,findData,dir },freeItor }; enumDir = function( dir,p="*.*",proc){ var queue = { dir }; var filter = { ["."]=1;[".."]=1 }; var fullpath; if( type(proc) != type.function ) error("请指定回调函数",2); do{ var curDir = ..table.pop(queue); for(dirName,fileName,findData,rootDir in this.eachDir(curDir,p)){ if( !fileName ){ if( !filter[dirName]){ fullpath = rootDir + dirName; ..table.push(queue,fullpath); } else continue; } else{ fullpath = dirName + fileName; } if( proc(dirName,fileName,fullpath,findData) === false )break; } }while (#queue) } open = function( path,mode,flag){ var f,err = file( this.connect,path,mode,flag ) return f,err; }; lastResponse = ..inet.lastResponse; } namespace ftp{ canonicalizeDirectory = function(path){ if(!path)error("目录不能为空",2); path = ..string.replace(path,"@@\", "/") ; if( not ..string.endsWith( path,"/") ) path = path + "/"; return path; }; getParentDir = function(path){ if( !#path ) return; path = ..string.replace(path,"@@\","/"); if( path == "/" ) return; if( ..string.endsWith(path,"/",true) ) path = ..string.left(path,-2); if( !#path ) return; var pdir = ..string.match( path,".*[/\\]" ); if( #pdir ){ return pdir; } else { return "./" } } FtpGetFile = ::Wininet.api("FtpGetFile","bool(POINTER hConnect,ustring remoteFile,ustring newFile,bool failIfExists,INT dwFlagsAndAttributes,INT dwFlags,INT dwContext)") FtpPutFile = Wininet.api("FtpPutFile","bool(POINTER hConnect,ustring localFile,ustring remoteFile, INT dwFlags,INT dwContext)") FtpRenameFile = Wininet.api("FtpRenameFile","bool(POINTER hConnect,USTRING lpszExisting,USTRING lpszNew)") FtpDeleteFile = Wininet.api("FtpDeleteFile","bool(POINTER hConnect,USTRING lpszFileName)") FtpRemoveDirectory = Wininet.api("FtpRemoveDirectory","bool(POINTER hConnect,USTRING lpszFileName)") FtpFindFirstFile = Wininet.api("FtpFindFirstFile","pointer(POINTER hFtpSession,USTRING lpszSearchFile,struct& lpFindFileData,int dwFlags,int dwContent)"); InternetFindNextFile = ::Wininet.api("InternetFindNextFile","int(pointer hFind,struct& lpvFindData)"); FtpSetCurrentDirectory = ::Wininet.api("FtpSetCurrentDirectoryW","bool(POINTER hFtpSession,ustring dir)"); FtpGetCurrentDirectory = ::Wininet.api("FtpGetCurrentDirectoryW","bool(POINTER hConnect,ustring &dir,INT &len)"); FtpCommand = ::Wininet.api("FtpCommand","bool(POINTER hConnect,bool expectResponse,INT flags,ustring command,pointer context,pointer &phFtpCommand)"); FtpCreateDirectory = ::Wininet.api("FtpCreateDirectory","bool(POINTER hConnect,USTRING dir)") class file{ ctor( hConnect,fileName,mode="rb",flags = 0){ var hFile; if(type(fileName)=="pointer"){ hFile = fileName; } else{ flags = flags | ( ..string.find(mode,"b") ? 2/*_FTP_TRANSFER_TYPE_BINARY*/ : 1/*_FTP_TRANSFER_TYPE_ASCII*/ ); if(!fileName) return null,"fileName 不能为空"; fileName = ..string.replace(fileName,"@@\", "/") ; mode = mode[1] == 'r'# ? 0x80000000/*_GENERIC_READ*/ : 0x40000000/*_GENERIC_WRITE*/; hFile = FtpOpenFile( hConnect,fileName,mode,flags); if(!hFile){ return null,..inet.lastResponse(); }; } ..table.gc(this,"close"); }; write = function(...){ return wirteFile(hFile,...); }; read = function(bufferSize){ if( bufferSize != -1 ){ return readFile(hFile,bufferSize); } else { return this.readAll(); } }; readAll = function(bufferSize){ var ret = [] for data in this.eachRead(){ ..table.push(ret,data); } return ..string.join(ret); } eachRead = function(bufferSize){ return function(){ return readFile(hFile,bufferSize); }; }; writeBuffer = function(buffer,l){ return writeBuffer(hFile,buffer,l); }; readBuffer = function(buffer,l){ return readBuffer(hFile,buffer,l); }; eachReadBuffer = function(buffer,l){ return function(){ return readBuffer(hFile,buffer,l); }; }; size64 = function(){ var low,high = FtpGetFileSize(hFile,0); return ..math.size64(low,high); }; size = function(){ var low,high = FtpGetFileSize(hFile,0); return tonumber(..math.size64(low,high)); }; close = function(){ if(hFile){ ::InternetCloseHandle(hFile); hFile = null; } }; } namespace file{ import inet.file; readFile = inet.file.read; wirteFile = inet.file.write; readBuffer = inet.file.readBuffer; writeBuffer = inet.file.writeBuffer; FtpOpenFile = ::Wininet.api("FtpOpenFile","pointer(POINTER hConnect,STRING fileName,INT dwAccess,INT flags,pointer contect)") FtpGetFileSize = ::Wininet.api("FtpGetFileSize","INT(POINTER hFile,INT &sizeHigh)"); }; _DATA_CONNECTION_COMMANDS = { "LIST":1, "NLST":1, "MLSD":1, // 目录列表 "RETR":1, // 下载 "STOR":1, "APPE":1, "STOU":1 // 上传 }; } /**intellisense() inet.ftp = FTP文件传输支持库 inet.ftp(.(主机,用户名,密码,端口号,是否被动模式,UserAgent,代理,绕过代理,选项) = 除参数 @1 需要指定主机地址以外,其他参数都是可选参数。\n被动模式(PASV/Passive mode) 默认为 true 。\n其他参数用法请参考 inet.http 构造参数。 inet.ftp.getParentDir(.(路径) = 返回父目录 !inet_ftp.eachDir = @for(dirName,fileName,findData,curDir in ??.eachDir("\")){ if( !fileName ){ io.print("目录名",dirName);__ } else{ io.print("文件名",fileName); } __/*curDir为当前目录\n如果当前是一个文件请忽略dirName参数\n如果当前是一个目录,fileName为空*/ } !inet_ftp.enumDir(目录,通配符,回调函数) = @.enumDir("\","*.*",\n function(dir,fileName,fullpath,findData ){\n io.print( fileName?"文件":"目录",fullpath )\n return true;__/*返回false停止*/\n } \n); !inet_ftp.setCurDir(.(目录) = 设置服务器上的当前目录 !inet_ftp.getCurDir() = 返回服务器上的当前目录,该函数保证返回的目录尾部是斜杠 !inet_ftp.deleteDir(.(目录) = 删除目录 !inet_ftp.createParentDir(.(目录) = 创建指定路径的父目录 !inet_ftp.createDir(.(目录) = 创建目录,支持创建多级目录\n可以斜杠开头的的路径表示相对于根目录的完整路径\n也可使用setCurDir()设定当前目录 !inet_ftp.delete(.(文件) = 删除文件 !inet_ftp.rename(.(文件名,新名字) = 文件改名 !inet_ftp.download(.("/本地路径","/远程路径") = 下载前可使用 setCurDir 方法设定服务端当前目录位置。\n也可以斜杠开头的的路径表示相对于根目录的完整路径 !inet_ftp.upload(.("/本地路径","远程路径") = 上传前可使用setCurDir()设定当前目录\n也可以斜杠开头的的路径表示相对于根目录的完整路径 !inet_ftp.download(.(loadfile,remotefile,1/*_INTERNET_FLAG_TRANSFER_ASCII*/) = ASCII 模式下载 !inet_ftp.download(.(loadfile,remotefile,2/*_INTERNET_FLAG_TRANSFER_BINARY*/) = BINARY 模式下载 !inet_ftp.upload(.(formfile,todir,1/*_INTERNET_FLAG_TRANSFER_ASCII*/) = ASCII 模式上传 !inet_ftp.upload(.(formfile,todir,2/*_INTERNET_FLAG_TRANSFER_BINARY*/) = BINARY 模式上传 !inet_ftp.command(.(commandString,flags) = @执行 FTP 命令。参数说明:\n1. 参数 @commandString 指定 FTP 命令。\n2. 可选参数 @flags 指定响应数据传送模式(数值),默认为二进制模式,\n指定为 1/*_FTP_TRANSFER_TYPE_ASCII*/ 则使用 ASCII 传送模式。\n\n执行 "LIST", "NLST", "MLSD", "RETR", "STOR", "APPE", "STOU" 等需要数据连接的 FTP 命令时\n返回 inet.ftp.file 对象(提供 readAll,eachRead 等方法l), 失败返回 null 。\n执行其他仅建立控制连接的 FTP 命令时成功返回服务器响应数据(字符串),失败返回 null 。 !inet_ftp.command() = !ftp_file. !inet_ftp.lastResponse() = 获取服务器最后发送的数据,成功返回字符串。\n调用此函数等价于调用 inet.lastResponse 函数。 !inet_ftp.close() = 关闭连接 !inet_ftp.open("/目录/文件路径","rb") = 二进制只读模式打开 FTP 文件流。\n成功返回 FTP 文件流对象,失败返回 null, 错误信息。\n\n参数 @2 为"rt"则使用文本模式,\n打开文件后仅允许读文件,其他所有 FTP 操作被禁用,\n直到调用 close 函数关闭文件。 !inet_ftp.open("/目录/文件路径","wb") = 二进制只写模式打开FTP 文件流。\n成功返回 FTP 文件流对象,失败返回 null, 错误信息。\n\n参数 @2 为"wt"则使用文本模式,\n打开文件后仅允许写文件,其他所有 FTP 操作被禁用,\n直到调用 close 函数关闭文件。 !inet_ftp.open() = !ftp_file. !ftp_file.size() = 返回文件大小 !ftp_file.size64() = 返回文件大小\n返回文件大小\n返回值为math.size64长整数对象\n!math_size64. !ftp_file.read() = 读文件,返回文件数据,\n可选指定缓冲区长度 !ftp_file.read(-1) = 读取所有数据 !ftp_file.readAll() = 读取所有数据 !ftp_file.write(.(写入字符串,不定个数字符串) = 写文件,支持多参数,\n返回写入数据的总长度,失败返回空或0 !ftp_file.writeBuffer(.(buffer,写入长度) = 上传文件数据\n参数一必须是使用 buffer 对象\n长度参数可选,默认为缓冲区长度. !ftp_file.readBuffer(.(buffer,写入长度) = 下载文件数据\n参数一必须是使用 buffer 对象\n长度参数可选,默认为缓冲区长度. !ftp_file.eachRead(缓冲区长度) = @for(str in ??.eachRead() ){ __ } !ftp_file.eachReadBuffer = @var buffer = ..raw.buffer( 1024 * 10 );\nfor( size in ??.eachReadBuffer( buffer ) ){ table.push(__,str ) ; } !ftp_file.close() = 关闭FTP文件 inet.ftp() = !inet_ftp. end intellisense**/ /**intellisense() _FTP_TRANSFER_TYPE_ASCII=@1/*_FTP_TRANSFER_TYPE_ASCII*/ _FTP_TRANSFER_TYPE_BINARY=@2/*_FTP_TRANSFER_TYPE_BINARY*/ _FTP_TRANSFER_TYPE_DISABLED=@0/*_FTP_TRANSFER_TYPE_DISABLED*/ end intellisense**/