 | |  |  | FTP单文件运行例子
- /*
- * AIWROK FTP 独立用法脚本(纯 Rhino / JDK,零 importClass、零第三方 dex、零外部模块)
- * 实现方式:用 java.net.Socket 手写 FTP 协议(RFC 959),原生支持:
- * 连接/登录、上传(STOR)、下载(RETR)、查询列目录(LIST)、当前目录(PWD)。
- * 注意:除 java.* 标准类外不引用任何资源,可直接运行。
- * 若列出中文文件名出现乱码,把 ftpList 里 InputStreamReader 的编码从 "ISO-8859-1" 改成 "GBK" 或 "UTF-8" 即可。
- */
- // ============ ① 顶部配置区(用户改这里即可跑) ============
- var FTP_HOST = "154.221.80.195"; // FTP 服务器地址
- var FTP_PORT = 21; // FTP 端口,默认 21
- var FTP_USER = "ghgfggggj"; // 登录用户名
- var FTP_PASS = "CrSaz4611bx1B"; // 登录密码
- var LOCAL_UPLOAD = "/sdcard/auto/aiwork_ftp_sample.txt"; // 要上传的本地文件(演示会自动生成,无需手动准备)
- var REMOTE_UPLOAD_PATH = "/aiwork_ftp_sample.txt"; // 上传后保存在远程的路径(含文件名,放根目录更安全)
- var REMOTE_DOWNLOAD_PATH = "/aiwork_ftp_sample.txt"; // 要从远程下载的文件路径(用刚上传的同名文件,演示自洽)
- var LOCAL_DOWNLOAD = "/sdcard/auto/aiwork_ftp_sample_downloaded.txt"; // 下载保存到本地的路径
- var LIST_DIR = "/"; // 要列出内容的远程目录,"/" 表示根目录
- // ============ 全局控制连接 ============
- var ctrlSock = null;
- var ctrlOut = null;
- var ctrlReader = null;
- // 读取一条控制回复(多行回复以 "code " 结尾,遇到即停)
- function readReply() {
- var full = "", code = "";
- while (true) {
- var line = ctrlReader.readLine();
- if (line == null) break;
- line = String(line); // 强制转成 JS 字符串:否则 Rhino 下 Java String 与 "220" 这种字面量用 !== 永远不等
- full += line + "\n";
- if (/^[0-9][0-9][0-9] /.test(line)) { code = line.substring(0, 3); break; }
- }
- return { code: code, text: full };
- }
- // 发送命令(强制 CRLF)
- function sendCmd(cmd) {
- // 用 java.lang.String 包一层保证有 getBytes;write 用三参形式避免 Rhino 把 write(byte[]) 误判成 write(int)
- var b = new java.lang.String(cmd + "\r\n").getBytes("ISO-8859-1");
- ctrlOut.write(b, 0, b.length);
- ctrlOut.flush();
- }
- // 解析 PASV 回复 -> { ip, port }
- function parsePasv(text) {
- var m = text.match(/\(([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+),([0-9]+)\)/);
- if (!m) return null;
- var ip = m[1] + "." + m[2] + "." + m[3] + "." + m[4];
- var port = (parseInt(m[5], 10) * 256) + parseInt(m[6], 10);
- return { ip: ip, port: port };
- }
- // 发 PASV 并打开被动数据连接
- function openPasvData() {
- var r = readReply(); // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2)
- var info = parsePasv(r.text);
- if (!info) { printl("[FTP] PASV 解析失败: " + r.text); return null; }
- // 数据连接优先用「控制连接的公网 IP(FTP_HOST) + PASV 端口」:
- // 很多服务器 PASV 回复里写的是内网 IP(NAT 缺陷),从外部连不通;
- // 而用我们连控制通道的公网 IP + PASV 端口通常可达(已实测 154.201.80.125 可行)。
- // 若公网连不通(如客户端与服务器同局域网、内网 IP 才通),回退到 PASV 回复里的 IP。
- var ds = null;
- try {
- ds = new java.net.Socket(FTP_HOST, info.port);
- } catch (e1) {
- try { ds = new java.net.Socket(info.ip, info.port); }
- catch (e2) { printl("[FTP] 数据连接失败: " + e2); return null; }
- }
- ds.setSoTimeout(15000);
- return ds;
- }
- // ============ 连接 / 登录 ============
- function ftpConnect() {
- try {
- ctrlSock = new java.net.Socket(FTP_HOST, FTP_PORT);
- ctrlSock.setSoTimeout(15000);
- ctrlOut = ctrlSock.getOutputStream();
- ctrlReader = new java.io.BufferedReader(new java.io.InputStreamReader(ctrlSock.getInputStream(), "ISO-8859-1"));
- var welcome = readReply();
- if (welcome.code !== "220") { printl("[FTP] 服务器未就绪(" + welcome.code + "): " + welcome.text); return false; }
- sendCmd("USER " + FTP_USER);
- readReply();
- sendCmd("PASS " + FTP_PASS);
- var r2 = readReply();
- if (r2.code !== "230") { printl("[FTP] 登录失败(" + r2.code + "): " + r2.text); return false; }
- sendCmd("TYPE I"); // 二进制传输,必须设置(否则图片/压缩包损坏)
- readReply();
- printl("[FTP] 连接成功");
- return true;
- } catch (e) {
- printl("[FTP] 连接异常: " + e);
- return false;
- }
- }
- // ============ 查询 / 列目录 ============
- function ftpList(dir) {
- try {
- if (ctrlSock == null) { printl("[FTP] 尚未连接,请先调用 ftpConnect()"); return -1; }
- var target = (dir == null || dir === "") ? "/" : dir;
- sendCmd("PASV");
- var ds = openPasvData();
- if (ds == null) return -1;
- sendCmd("LIST " + target);
- readReply(); // 150 开始
- var dr = new java.io.BufferedReader(new java.io.InputStreamReader(ds.getInputStream(), "ISO-8859-1"));
- var line, count = 0;
- printl("[FTP] 目录 " + target + " 内容如下:");
- while ((line = dr.readLine()) != null) {
- printl(" " + line);
- count++;
- }
- dr.close(); ds.close();
- readReply(); // 226 结束
- printl("[FTP] 目录共 " + count + " 个条目");
- return count;
- } catch (e) {
- printl("[FTP] 列目录异常: " + e);
- return -1;
- }
- }
- // ============ 上传 ============
- function ftpUpload(localPath, remotePath) {
- var fis = null, ds = null;
- try {
- if (ctrlSock == null) { printl("[FTP] 尚未连接,请先调用 ftpConnect()"); return false; }
- var f = new java.io.File(localPath);
- if (!f.exists()) { printl("[FTP] 本地文件不存在: " + localPath); return false; }
- sendCmd("PASV");
- ds = openPasvData();
- if (ds == null) return false;
- sendCmd("STOR " + remotePath);
- readReply(); // 150 开始
- var dout = ds.getOutputStream();
- fis = new java.io.FileInputStream(f);
- var buf = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 4096);
- var len;
- while ((len = fis.read(buf)) > 0) { dout.write(buf, 0, len); }
- fis.close(); fis = null;
- dout.flush(); ds.close(); ds = null;
- var done = readReply(); // 226 结束
- var ok = (done.code === "226" || done.code === "250");
- printl(ok ? ("[FTP] 上传成功: " + localPath + " -> " + remotePath) : ("[FTP] 上传失败(" + done.code + "): " + done.text));
- return ok;
- } catch (e) {
- printl("[FTP] 上传异常: " + e);
- return false;
- } finally {
- if (fis != null) try { fis.close(); } catch (x) {}
- if (ds != null) try { ds.close(); } catch (x) {}
- }
- }
- // ============ 下载 ============
- function ftpDownload(remotePath, localPath) {
- var fos = null, ds = null;
- try {
- if (ctrlSock == null) { printl("[FTP] 尚未连接,请先调用 ftpConnect()"); return false; }
- var lf = new java.io.File(localPath);
- var parent = lf.getParentFile();
- if (parent != null && !parent.exists()) parent.mkdirs();
- sendCmd("PASV");
- ds = openPasvData();
- if (ds == null) return false;
- sendCmd("RETR " + remotePath);
- var r = readReply(); // 150 开始
- if (r.code !== "150" && r.code !== "125") { printl("[FTP] 下载无法开始(" + r.code + "): " + r.text); ds.close(); return false; }
- var din = ds.getInputStream();
- fos = new java.io.FileOutputStream(lf);
- var buf = java.lang.reflect.Array.newInstance(java.lang.Byte.TYPE, 4096);
- var len;
- while ((len = din.read(buf)) > 0) { fos.write(buf, 0, len); }
- fos.close(); fos = null;
- ds.close(); ds = null;
- var done = readReply(); // 226 结束
- var ok = (done.code === "226" || done.code === "250");
- printl(ok ? ("[FTP] 下载成功: " + remotePath + " -> " + localPath) : ("[FTP] 下载失败(" + done.code + "): " + done.text));
- return ok;
- } catch (e) {
- printl("[FTP] 下载异常: " + e);
- return false;
- } finally {
- if (fos != null) try { fos.close(); } catch (x) {}
- if (ds != null) try { ds.close(); } catch (x) {}
- }
- }
- // ============ 当前工作目录 ============
- function ftpPwd() {
- try {
- sendCmd("PWD");
- var r = readReply(); // 257 "/xxx" is current directory
- var m = r.text.match(/"(.*)"/);
- return m ? m[1] : r.text;
- } catch (e) { return ""; }
- }
- // ============ 关闭连接 ============
- function ftpClose() {
- try {
- if (ctrlSock != null) {
- try { sendCmd("QUIT"); readReply(); } catch (x) {}
- try { ctrlSock.close(); } catch (x) {}
- printl("[FTP] 连接已关闭");
- }
- } catch (e) { printl("[FTP] 关闭异常: " + e); }
- }
- // ============ 辅助:自动生成本地样例文件(演示用) ============
- function ensureLocalSample(localPath) {
- try {
- var f = new java.io.File(localPath);
- if (f.exists()) return true;
- var p = f.getParentFile();
- if (p != null && !p.exists()) p.mkdirs();
- var fw = new java.io.FileWriter(f);
- fw.write("AIWROK FTP 演示样例文件\n生成时间: " + ("" + java.lang.System.currentTimeMillis()));
- fw.close();
- printl("[FTP] 已自动生成本地样例文件: " + localPath);
- return true;
- } catch (e) { printl("[FTP] 生成样例文件失败: " + e); return false; }
- }
- // ============ main 演示(连接→PWD→列目录→上传→列目录→下载→关闭) ============
- function main() {
- printl("========== FTP 独立用法演示开始(纯 Socket,无 importClass) ==========");
- if (!ftpConnect()) { printl("[FTP] 连接失败,演示终止"); return; }
- printl("[FTP] 当前工作目录: " + ftpPwd());
- printl("----- 上传前列出 " + LIST_DIR + " -----");
- ftpList(LIST_DIR);
- ensureLocalSample(LOCAL_UPLOAD);
- ftpUpload(LOCAL_UPLOAD, REMOTE_UPLOAD_PATH);
- printl("----- 上传后列出 " + LIST_DIR + " -----");
- ftpList(LIST_DIR);
- ftpDownload(REMOTE_DOWNLOAD_PATH, LOCAL_DOWNLOAD);
- ftpClose();
- printl("========== FTP 独立用法演示结束 ==========");
- }
- main();
复制代码
| |  | |  |
|