三、基本功能
1、登陆
采用FTP进行文件传输,其实本质上还是采用Java.net.socket进行通信。以下代码只是类net.sf.jftp.net.FtpConnection其中一个login方法。当然在下面的代码,为了节省版面,以及将一些原理阐述清楚,笔者将一些没必要的代码去掉了,如日志等代码。完整的代码请参考J-ftp的源代码或是笔者所以的示例源代码,后面的代码示例也同理:
public int login(String username, String password) { this.username = username; this.password = password; int status = LOGIN_OK; jcon = new JConnection(host, port); if(jcon.isThere()) { in = jcon.getReader(); if(getLine(POSITIVE) == null)//FTP220_SERVICE_READY) == null) { ok = false; status = OFFLINE; } if(!getLine(loginAck).startsWith(POSITIVE))//FTP230_LOGGED_IN)) { if(success(POSITIVE))//FTP230_LOGGED_IN)) { } else { ok = false; status = WRONG_LOGIN_DATA; } } } else { if(msg) { Log.debug("FTP not available!"); ok = false; status = GENERIC_FAILED; } } if(ok) { connected = true; system(); binary(); String[] advSettings = new String[6]; if(getOsType().indexOf("OS/2") >= 0) { LIST_DEFAULT = "LIST"; } if(LIST.equals("default")) { //just get the first item (somehow it knows first is the //FTP list command) advSettings = LoadSet.loadSet(Settings.adv_settings); //*** IF FILE NOT FOUND, CREATE IT AND SET IT TO LIST_DEFAULT if(advSettings == null) { LIST = LIST_DEFAULT; SaveSet s = new SaveSet(Settings.adv_settings, LIST); } else { LIST = advSettings[0]; if(LIST == null) { LIST = LIST_DEFAULT; } } } if(getOsType().indexOf("MVS") >= 0) { LIST = "LIST"; } //*** fireDirectoryUpdate(this); fireConnectionInitialized(this); } else { fireConnectionFailed(this, new Integer(status).toString()); } return status; }
此登陆方法中,有一个JConnection类,此类负责建立socket套接字 ,同时,此类是一种单独的线程,这样的好处是为了配合界面的变化,而将网络的套接字连接等工作做为单独的线程来处理,有利于界面的友好性。下面是net.sf.jftp.net.JConnection类的run方法,当然,此线程的启动是在JConnection类的构造方法中启动的。
2、上传下载
文件的上传可以分成多线程及单线程,在单线程情况下比较简单,而在多线程的情况下,要处理的事情要多点,同时也要小心很多。下面是net.sf.jftp.net.FtpConnection的上传handleUpload方法。已经考虑了单线程及多线程两种不同的类型。
此run方法中的socket这里说明一下,此类实现客户端套接字(也可以就叫“套接字”),套接字是两台机器之间的通信端点。套接字的实际工作由 SocketImpl 类的实例执行。应用程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接字。具体的说明请参考JDK5 的API说明,最好是中文的。呵呵。public void run() { try { s = new Socket(host, port); localPort = s.getLocalPort(); //if(time > 0) s.setSoTimeout(time); out = new PrintStream(new BufferedOutputStream(s.getOutputStream(), Settings.bufferSize)); in = new BufferedReader(new InputStreamReader(s.getInputStream()), Settings.bufferSize); isOk = true; // } } catch(Exception ex) { ex.printStackTrace(); Log.out("WARNING: connection closed due to exception (" + host + ":" + port + ")"); isOk = false; try { if((s != null) && !s.isClosed()) { s.close(); } if(out != null) { out.close(); } if(in != null) { in.close(); } } catch(Exception ex2) { ex2.printStackTrace(); Log.out("WARNING: got more errors trying to close socket and streams"); } } established = true; }
2、上传下载
文件的上传可以分成多线程及单线程,在单线程情况下比较简单,而在多线程的情况下,要处理的事情要多点,同时也要小心很多。下面是net.sf.jftp.net.FtpConnection的上传handleUpload方法。已经考虑了单线程及多线程两种不同的类型。
public int handleUpload(String file, String realName) { if(Settings.getEnableMultiThreading() && (!Settings.getNoUploadMultiThreading())) { Log.out("spawning new thread for this upload."); FtpTransfer t; if(realName != null) { t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(), file, username, password, Transfer.UPLOAD, handler, listeners, realName, crlf); } else { t = new FtpTransfer(host, port, getLocalPath(), getCachedPWD(), file, username, password, Transfer.UPLOAD, handler, listeners, crlf); } lastTransfer = t; return NEW_TRANSFER_SPAWNED; } else { if(Settings.getNoUploadMultiThreading()) { Log.out("upload multithreading is disabled."); } else { Log.out("multithreading is completely disabled."); } return (realName == null) ? upload(file) : upload(file, realName); } }
在多线程的情况下,有一个单独的类net.sf.jftp.net .FtpTransfer,当然,多线程情况下,此类肯定是一个单独的线程了。与JConnection相似,其线程的启动也是在构造方法中启动。而在它的run方法中,进行文件的读取及传输。
public void run() { if(handler.getConnections().get(file) == null) { handler.addConnection(file, this); } else if(!pause) { Log.debug("Transfer already in progress: " + file); work = false; stat = 2; return; } boolean hasPaused = false; while(pause) { try { runner.sleep(100); if(listeners != null) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, PAUSED, -1); } } if(!work) { if(listeners != null) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, REMOVED, -1); } } } } catch(Exception ex) { } hasPaused = true; } while((handler.getConnectionSize() >= Settings.getMaxConnections()) && (handler.getConnectionSize() > 0) && work) { try { stat = 4; runner.sleep(400); if(!hasPaused && (listeners != null)) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, QUEUED, -1); } } else { break; } } catch(Exception ex) { ex.printStackTrace(); } } if(!work) { if(listeners != null) { for(int i = 0; i < listeners.size(); i++) { ((ConnectionListener) listeners.elementAt(i)).updateProgress(file, REMOVED, -1); } } handler.removeConnection(file); stat = 3; return; } started = true; try { runner.sleep(Settings.ftpTransferThreadPause); } catch(Exception ex) { } con = new FtpConnection(host, port, remotePath, crlf); con.setConnectionHandler(handler); con.setConnectionListeners(listeners); int status = con.login(user, pass); if(status == FtpConnection.LOGIN_OK) { File f = new File(localPath); con.setLocalPath(f.getAbsolutePath()); if(type.equals(UPLOAD)) { if(newName != null) { transferStatus = con.upload(file, newName); } else { transferStatus = con.upload(file); } } else { transferStatus = con.download(file,this.newName); } } if(!pause) { handler.removeConnection(file); } }
至于下载的过程,因为它是上传的逆过程,与上传的方法及写法大同小异,在些出于篇幅的考虑,并没有将代码列出,但其思想及思路完全一样。请读者参考源代码。