YYPOST群发软件 发表于 前天 09:14

AIWROK软件安卓脚本FTP上传下载例子


AIWROK软件安卓脚本FTP上传下载例子






/**
* AIWROK 简易FTP工具 - 纯Java Socket实现
* 交流QQ群: 711841924 (群一) / 528816639 (苹果脚本内测群)s
*/

// ==================== FTP配置 ====================
// 配置FTP服务器连接信息
var FTPConfig = {
    host: "38.67.75.157",      // FTP服务器地址
    port: 21,                   // FTP端口(默认21)
    username: "fghgdfhfdh",         // 登录用户名
    password: "aH6CEtK6X77J",   // 登录密码
    timeout: 60000,             // 连接超时时间(毫秒)
    encoding: "UTF-8"         // 字符编码
};

// ==================== FTP客户端 ====================
// 使用IIFE(立即执行函数)创建FTPClient对象
// 封装所有FTP操作,保持全局命名空间清洁
var FTPClient = (function() {
    // 私有变量 - FTP连接状态
    var controlSocket = null;   // 控制通道Socket(发送命令)
    var dataSocket = null;      // 数据通道Socket(传输文件)
    var reader = null;          // 控制通道读取器
    var writer = null;          // 控制通道写入器
    var isConnected = false;    // 连接状态标志

    /**
   * 读取FTP服务器响应
   * 从控制通道读取一行文本
   * @returns {string|null} 服务器响应的文本,失败返回null
   */
    function readResponse() {
      try {
            var line = reader.readLine();
            if (line) console.log("FTP: " + line);
            return line;
      } catch (e) {
            return null;
      }
    }

    /**
   * 发送FTP命令并等待响应
   * @param {string} cmd - FTP命令(如 USER, PASS, PASV等)
   * @returns {string|null} 服务器响应,失败返回null
   */
    function sendCommand(cmd) {
      try {
            writer.write(cmd + "\r\n");// FTP命令以\r\n结尾
            writer.flush();            // 立即发送
            sleep.second(0.5);         // 等待服务器响应
            return readResponse();       // 读取响应
      } catch (e) {
            return null;
      }
    }

    /**
   * 连接FTP服务器并登录
   * 建立控制通道,完成用户认证
   * @param {Object} config - FTP配置对象(host, port, username, password等)
   * @returns {boolean} 连接成功返回true,失败返回false
   */
    function connect(config) {
      try {
            console.log("连接FTP: " + config.host);
            
            // 1. 创建控制通道Socket连接
            controlSocket = new java.net.Socket(config.host, config.port);
            controlSocket.setSoTimeout(config.timeout);// 设置超时
            
            // 2. 创建输入输出流(使用指定编码)
            var InputStreamReader = java.io.InputStreamReader;
            var OutputStreamWriter = java.io.OutputStreamWriter;
            var BufferedReader = java.io.BufferedReader;
            var BufferedWriter = java.io.BufferedWriter;
            
            reader = new BufferedReader(new InputStreamReader(controlSocket.getInputStream(), config.encoding));
            writer = new BufferedWriter(new OutputStreamWriter(controlSocket.getOutputStream(), config.encoding));
            
            // 3. 读取服务器欢迎消息(220开头)
            var welcome = readResponse();
            if (!welcome || !welcome.startsWith("220")) {
                disconnect();
                return false;
            }
            
            // 4. 读取所有多行欢迎消息(Pure-FTPd会发送多条220-开头的消息)
            sleep.second(0.3);
            var line;
            while ((line = reader.readLine()) !== null) {
                if (!line.startsWith("220-")) break;// 遇到非220-的行就停止
            }
            
            // 5. 发送用户名(USER命令)
            var resp = sendCommand("USER " + config.username);
            if (!resp || !resp.startsWith("331")) {// 331表示需要密码
                disconnect();
                return false;
            }
            
            // 6. 发送密码(PASS命令)
            resp = sendCommand("PASS " + config.password);
            if (!resp || !resp.startsWith("230")) {// 230表示登录成功
                disconnect();
                return false;
            }
            
            console.log("✓ 登录成功");
            isConnected = true;
            return true;
            
      } catch (e) {
            console.log("连接失败: " + e.message);
            disconnect();
            return false;
      }
    }

    /**
   * 设置被动模式(PASV)
   * FTP被动模式:服务器开启数据端口,客户端连接
   * 自动处理内网IP映射(将10.x.x.x替换为公网IP)
   * @returns {boolean} 设置成功返回true,失败返回false
   */
    function setupPassiveMode() {
      try {
            // 关闭旧的数据通道
            if (dataSocket) {
                try { dataSocket.close(); } catch(e) {}
                dataSocket = null;
            }
            
            // 发送PASV命令,获取服务器数据端口
            var resp = sendCommand("PASV");
            if (!resp || !resp.startsWith("227")) return false;// 227表示进入被动模式
            
            // 解析PASV响应: 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
            var match = resp.match(/\(([^)]+)\)/);
            if (!match) return false;
            
            var parts = match.split(",");
            var port = parseInt(parts) * 256 + parseInt(parts);// 计算端口号
            var ip = parts + "." + parts + "." + parts + "." + parts;
            
            // 重要:如果服务器返回内网IP,替换为配置的公网IP
            // 这是因为FTP服务器在NAT后面,会返回内网地址
            if (ip.startsWith("10.") || ip.startsWith("192.168.")) {
                console.log("检测到内网IP: " + ip + ",使用服务器地址: " + FTPConfig.host);
                ip = FTPConfig.host;
            }
            
            // 创建数据通道Socket连接
            dataSocket = new java.net.Socket(ip, port);
            dataSocket.setSoTimeout(60000);// 数据传输超时60秒
            return true;
      } catch (e) {
            console.log("被动模式设置失败: " + e.message);
            return false;
      }
    }

    /**
   * 上传文件到FTP服务器
   * 使用被动模式传输,支持二进制文件
   * @param {string} localPath - 本地文件路径(如 /sdcard/test.txt)
   * @param {string} remotePath - 远程文件路径(如 /test.txt)
   * @returns {boolean} 上传成功返回true,失败返回false
   */
    function upload(localPath, remotePath) {
      if (!isConnected) return false;
      
      console.log("上传: " + localPath + " -> " + remotePath);
      
      // 检查本地文件是否存在
      var file = new java.io.File(localPath);
      if (!file.exists()) {
            console.log("文件不存在: " + localPath);
            return false;
      }
      
      // 设置被动模式,建立数据通道
      if (!setupPassiveMode()) return false;
      
      // 发送STOR命令,准备接收文件
      var resp = sendCommand("STOR " + remotePath);
      if (!resp || !resp.startsWith("150")) return false;// 150表示准备接收
      
      // 读取本地文件并发送到数据通道
      var fis = new java.io.BufferedInputStream(new java.io.FileInputStream(file));
      var out = dataSocket.getOutputStream();
      var buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 4096);// 4KB缓冲区
      var len;
      
      while ((len = fis.read(buffer)) !== -1) {
            out.write(buffer, 0, len);// 写入数据通道
      }
      
      // 关闭流和Socket
      fis.close();
      out.close();
      dataSocket.close();
      dataSocket = null;
      
      // 读取最终响应(226表示传输完成)
      sleep.second(0.5);
      var finalResp;
      while ((finalResp = readResponse()) !== null) {
            if (finalResp.startsWith("226 ")) {// 226表示文件传输成功
                console.log("✓ 上传成功");
                return true;
            }
      }
      
      return false;
    }

    /**
   * 从FTP服务器下载文件
   * 使用被动模式传输,支持二进制文件
   * @param {string} remotePath - 远程文件路径(如 /test.txt)
   * @param {string} localPath - 本地保存路径(如 /sdcard/test.txt)
   * @returns {boolean} 下载成功返回true,失败返回false
   */
    function download(remotePath, localPath) {
      if (!isConnected) return false;
      
      console.log("下载: " + remotePath + " -> " + localPath);
      
      // 设置被动模式,建立数据通道
      if (!setupPassiveMode()) return false;
      
      // 发送RETR命令,请求下载文件
      var resp = sendCommand("RETR " + remotePath);
      if (!resp || !resp.startsWith("150")) return false;// 150表示准备发送
      
      // 从数据通道接收数据并保存到本地文件
      var fos = new java.io.BufferedOutputStream(new java.io.FileOutputStream(new java.io.File(localPath)));
      var input = dataSocket.getInputStream();
      var buffer = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 4096);// 4KB缓冲区
      var len;
      var total = 0;
      
      console.log("开始接收...");
      try {
            var startTime = new Date().getTime();
            var timeout = 30000;// 30秒超时保护
            
            while ((len = input.read(buffer)) !== -1) {
                fos.write(buffer, 0, len);// 写入本地文件
                total += len;
               
                // 检查是否超时
                var elapsed = new Date().getTime() - startTime;
                if (elapsed > timeout) {
                  console.log("超时: 已接收 " + total + " 字节");
                  break;
                }
            }
            console.log("接收完成: " + total + " 字节");
      } catch (e) {
            console.log("接收出错: " + e.message);
      }
      
      // 关闭流和Socket
      fos.close();
      input.close();
      dataSocket.close();
      dataSocket = null;
      
      // 读取最终响应(226表示传输完成)
      sleep.second(0.5);
      var finalResp;
      while ((finalResp = readResponse()) !== null) {
            if (finalResp.startsWith("226 ")) {// 226表示文件传输成功
                console.log("✓ 下载成功");
                return true;
            }
      }
      
      return false;
    }

    /**
   * 获取FTP目录列表
   * 使用LIST命令获取指定目录的文件列表
   * @param {string} remotePath - 远程目录路径(如 /)
   * @returns {Array} 文件列表数组,每个元素是一行LIST输出
   */
    function listDir(remotePath) {
      if (!isConnected) return [];
      
      console.log("列出目录: " + remotePath);
      
      // 设置被动模式
      if (!setupPassiveMode()) return [];
      
      // 发送LIST命令
      var resp = sendCommand("LIST " + remotePath);
      if (!resp || !resp.startsWith("150")) return [];// 150表示准备发送数据
      
      // 从数据通道读取目录列表
      var dirReader = new java.io.BufferedReader(
            new java.io.InputStreamReader(dataSocket.getInputStream(), "UTF-8")
      );
      
      var files = [];
      var line;
      while ((line = dirReader.readLine()) !== null) {
            if (line.trim()) files.push(line);// 跳过空行
      }
      
      // 关闭读取器和Socket
      dirReader.close();
      dataSocket.close();
      dataSocket = null;
      
      // 读取最终响应(226表示传输完成)
      sleep.second(0.5);
      var finalResp;
      while ((finalResp = readResponse()) !== null) {
            if (finalResp.startsWith("226 ")) break;// 找到最终的226响应
      }
      
      console.log("找到 " + files.length + " 个条目");
      return files;
    }

    /**
   * 断开FTP连接
   * 关闭所有Socket和流,释放资源
   */
    function disconnect() {
      try {
            // 关闭数据通道
            if (dataSocket) {
                try { dataSocket.close(); } catch(e) {}
                dataSocket = null;
            }
            
            // 关闭控制通道
            if (controlSocket) {
                try {
                  writer.write("QUIT\r\n");// 发送QUIT命令
                  writer.flush();
                } catch(e) {}
                try { controlSocket.close(); } catch(e) {}
                controlSocket = null;
            }
            
            // 关闭流
            if (reader) { try { reader.close(); } catch(e) {} reader = null; }
            if (writer) { try { writer.close(); } catch(e) {} writer = null; }
            
            isConnected = false;
            console.log("已断开");
      } catch (e) {
            // 忽略断开时的错误
      }
    }

    // 公开API - 只暴露这5个方法
    return {
      connect: connect,       // 连接FTP服务器
      disconnect: disconnect, // 断开连接
      upload: upload,         // 上传文件
      download: download,   // 下载文件
      listDir: listDir      // 获取目录列表
    };
})();

// ==================== 测试代码 ====================
/**
* 主函数 - 演示FTP工具的三个核心功能
* 1. 连接FTP服务器
* 2. 查看目录列表
* 3. 上传文件
* 4. 下载文件
* 5. 断开连接
*/
function main() {
    console.log("========== FTP简易工具 ==========\n");
   
    // 1. 连接FTP服务器
    if (!FTPClient.connect(FTPConfig)) {
      console.log("连接失败");
      return;
    }
   
    sleep.second(1);// 等待1秒
   
    // 2. 查看根目录文件列表
    console.log("\n--- 查看根目录 ---");
    var files = FTPClient.listDir("/");
    for (var i = 0; i < files.length; i++) {
      console.log("" + files);
    }
   
    sleep.second(1);// 等待1秒
   
    // 3. 上传文件测试
    console.log("\n--- 上传测试 ---");
    FTPClient.upload("/sdcard/test.txt", "/test_upload.txt");
   
    sleep.second(1);// 等待1秒
   
    // 4. 下载文件测试
    console.log("\n--- 下载测试 ---");
    FTPClient.download("/2.txt", "/sdcard/2_downloaded.txt");
   
    sleep.second(1);// 等待1秒
   
    // 5. 断开连接
    FTPClient.disconnect();
   
    console.log("\n========== 完成 ==========");
}

// 执行主函数
main();


页: [1]
查看完整版本: AIWROK软件安卓脚本FTP上传下载例子