当前位置:编程学习 > JSP >>

java ftp文件下载上传的实现方法

java ftp文件下载上传的实现方法

package waf.net.ftp;

import sun.net.ftp.*;
import java.io.*;

import sun.net.*;

public class FtpClient
{

 private sun.net.ftp.FtpClient client=null;
 private String strServerAddr="";
 private int iServerPort=0;
 private String strUserName="";
 private String strUserPwd="";
 
 private int iRetryTimes=0;
 
 
 
 // 监测连接是否正常
 public boolean CheckConn()
 {
  boolean bRet=false;
  try
  {
   client.pwd();
   bRet=true;
  }
  catch(IOException ex)
  {
   bRet=false;
  }
 
  return bRet;
 }
 
 private  boolean Binary()
 {
  boolean bRet=false;
  try
  {
   client.binary();
   bRet=true;
  }
  catch(IOException ex)
  {
   bRet=false;
  }
  return bRet;
 }
 private  boolean Ascii()
 {
  boolean bRet=false;
  try
  {
   client.ascii();
   bRet=true;
  }
  catch(IOException ex)
  {
   bRet=false;
  }
  return bRet;
 }
 public boolean GetTexFile(String strSrcFile,String strDestFile)
 {
  this.Ascii();
  return GetFile(strSrcFile,strDestFile);
 }
 
 public boolean GetBinFile(String strSrcFile,String strDestFile)
 {
  this.Binary();
  return GetFile(strSrcFile,strDestFile);
 }
 
 public boolean DisConnect()
 {
  boolean bRet=false;
  try
  {
   client.closeServer();
   bRet=true;
  }
  catch(IOException ex)
  {
   bRet=false;
  }
  return bRet; 
 
 }
 public boolean GetBinFileWithReConnect(String strSrcFile,String strDestFile)
 {
  return GetBinFileWithReConnect(strSrcFile,strDestFile,3);
 }
 public boolean GetBinFileWithReConnect(String strSrcFile,String strDestFile,int

iReTryTims)
 {
  boolean bRet=false;
 
  iRetryTimes++;
 
  // 尝试三次,失败放弃
  if(iRetryTimes>=iReTryTims)
  {
   return false;
  }
 
  if(GetBinFile(strSrcFile,strDestFile))
  {
   bRet=true;
   iRetryTimes=0;
  }
  else
  {
   bRet=false;
   try
   {
    Thread.sleep(200);
   }
   catch (InterruptedException e)
   {
    e.printStackTrace();
   }
   // 如果下载失败,就先监测一下连接状态,否则连接失效,就重新连接
   if(!this.CheckConn())
   {
    // 如果是连接失效了,就重连,如果不是连接失效,那么就是文件不存在等错误,就放


    if(ConnectTillSuc())
    {
     if(GetBinFileWithReConnect(strSrcFile,strDestFile,iReTryTims))
     {
      bRet=true;
      iRetryTimes=0;     
     }
    
    }
   }  
  }
 
  return bRet;
 }
 
 public boolean PutFile(String strSrcFile,String strDestFile)
 {
  boolean bRet=false;
 
  try
        {
   TelnetOutputStream fput=client.put(strDestFile);
  
   DataOutputStream puts = new DataOutputStream(fput);
  
   File fi = new File(strSrcFile);  
   RandomAccessFile srcFile = new RandomAccessFile(fi,"rw");  
  
   srcFile.seek(0);
           
            // 字节数组方式
            boolean bReadBytes=true;
           
            if(bReadBytes)
            {
             int iBufLen=4096;
             byte[] b=new byte[iBufLen];
             while(true)
             {
              int iRead=srcFile.read(b);
              if(iRead<0)
              {
               break;
              }
              puts.write(b,0,iRead);
             }
            }
            else
            {
             int ch;
       while ((ch = srcFile.read()) >= 0)
       {
        puts.write(ch);
                }            
            }

            puts.close();
   fput.close();
   srcFile.close();
   bRet=true;
  
   System.out.print("put file suc from "+strSrcFile+"   to 

"+strDestFile+"rn");
        }
  catch (IOException ex)
  {  
   bRet=false;
  
   ex.printStackTrace();
  } 
  return bRet;
 }
 public boolean GetFile(String strSrcFile,String strDestFile)
 {
  boolean bRet=false;
 
  try
        {
   TelnetInputStream fget=client.get(strSrcFile);
  
   DataInputStream puts = new DataInputStream(fget);
  
   File fi = new File(strDestFile);  
   RandomAccessFile getFile = new RandomAccessFile(fi,"rw");  
  
            getFile.seek(0);
           
            // 字节数组方式
            boolean bReadBytes=true;
           
            if(bReadBytes)
            {
             int iBufLen=4096;
             byte[] b=new byte[iBufLen];
             while(true)
             {
              int iRead=puts.read(b);
              if(iRead<0)
              {
               break;
              }
              getFile.write(b,0,iRead);
             }
            }
            else
            {
             int ch;
       while ((ch = puts.read()) >= 0)
       {
                    getFile.write(ch);
                }            
            }

   fget.close();
   getFile.close();
   bRet=true;
  
   System.out.print("get file suc from "+strSrcFile+"   to 

"+strDestFile+"rn");
        }
  catch (IOException ex)
  {  
   bRet=false;
  
   ex.printStackTrace();
  } 
  return bRet;
 }
 public boolean Connect(String ServerAddr,int ServerPort,String UserName,String

UserPwd)
 {
  boolean bRet=false;
  client=new sun.net.ftp.FtpClient();
 
  this.strServerAddr=ServerAddr;
  this.iServerPort=ServerPort;
  this.strUserName=UserName;
  this.strUserPwd=UserPwd;
 
  try
        { 
   client.openServer(strServerAddr,iServerPort);
   client.login(strUserName, strUserPwd);
   System.out.print("connect succeedn");
   bRet=true;
        }
  catch (IOException ex)
  {
   ex.printStackTrace();
   bRet=false;
  }
 
  return bRet;
 }
 
 public boolean ConnectTillSuc()
 {
  return this.ConnectTillSuc(this.strServerAddr, this.iServerPort,

this.strUserName, this.strUserPwd);
 }
 // 连接,直到成功
 public boolean ConnectTillSuc(String ServerAddr,int ServerPort,String

UserName,String UserPwd)
 {
  while(!this.Connect(ServerAddr, ServerPort, UserName, UserPwd))
  {
   try
   {
    System.out.print("connect failed,retry...n");
    Thread.sleep(3000);
   }
   catch (InterruptedException e)
   {
    e.printStackTrace();
   }
  }
  return true;
 }
 
 public static void main(String[] args) throws IOException, InterruptedException
 { 
  FtpClient client=new FtpClient();
 
//  常规方式 
//  client.GetBinFile("/abc/jdom.jar", "d:jdom.jar"); 
//  client.Connect("127.0.0.1",21,"abc","abc");
 
 
//  自动重连和重试下载方式 
//  client.ConnectTillSuc("127.0.0.1",21,"abc","abc");
//  for(int i=0;i<1000;i++)
//  {
//   client.GetBinFileWithReConnect("/abc/jdom.jar", "d:testjdom"+i+".jar");
//  }
 
 
  boolean b=client.ConnectTillSuc("127.0.0.1",21,"wd","wd");
 
  for(int i=0;i<1;i++)
  {
   client.PutFile("D:temp2010-02-01.txt", "/2010-02-01.txt");
//   System.out.print("get beginn");
//   client.GetBinFileWithReConnect("x:091125904094Record20070208

13911463641_20070208163821.vox","d:testt"+i+".vox");
//   client.GetBinFileWithReConnect("x:091125904094

Record20060531181647_13409182464.VOX","d:testt"+i+".vox");
//   System.out.print("get endnn");
   Thread.sleep(1000*120);
  }
 
 
  client.DisConnect();
 
 }
}

目前,已经有很多公开免费的ftp客户端类库,如simpleftp、J-ftp等,还有很多其他的

ftpclient。如下表所示,表中未能全部列出,如读者有更好的客户端FTP类库,请进行进一

步的补充。

FTP客户端类库名 备注

J-ftp http://jaist.dl.sourceforge.net/sourceforge/j-ftp/j-ftp-1.50.tar.gz
simpleftp HTTP://www.jibble.org/files/simpleftp.jar
ftpclient com.enterprisedt.net.ftp.FTPClient
FTPProtocol com.ibm.network.ftp.protocol.FTPProtocol
FtpConnection net.sf.jftp.net.FtpConnection
FTPClient org.apache.commons.net.ftp.FTPClient
FTPClient jshop.jnet.FTPClient
FtpClient sun.net.ftp.FtpClient
FTP com.cqs.ftp.FTP
Ftp cz.dhl.ftp.Ftp
FTPClient org.globus.io.ftp.FTPClient


  在本文中,笔者采用是J-ftp。这个是个开源的且功能十分强大的客户端FTP类库。笔者

很喜欢,同时也向各位读者推荐一下。算了免费为它做一个广告。

  三、基本功能 

  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类的构造方法中启动的。

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;

}此run方法中的socket这里说明一下,此类实现客户端套接字(也可以就叫“套接字”),

套接字是两台机器之间的通信端点。套接字的实际工作由 SocketImpl 类的实例执行。应用

程序通过更改创建套接字实现的套接字工厂可以配置它自身,以创建适合本地防火墙的套接

字。具体的说明请参考JDK5 的API说明,最好是中文的。呵呵。

  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);
}
}

  

至于下载的过程,因为它是上传的逆过程,与上传的方法及写法大同小异,在些出于篇

幅的考虑,并没有将代码列出,但其思想及思路完全一样。请读者参考源代码。

  四、进度条

  可以想象,如果在上传或是下载的过程中,没有任何的提示,用户根本没法判断任务是

否完成或是任务是否死了,常常由于上传时间或下载时间过长而误导用户。因此,进度条就

显得非常的重要与实用。

  进度条的实现,其实说起来很简单。就是在程序中开启两个线程,第一个线程用于动态

的改变界面上进度条的value值,而第二个线程则在上传或是下载的过程中,做成一个循环,

在此循环中,每次读取一定数量如8192字节数的数据。然后传完此数据后,调用第一个线程

中的updateProgress方法,来更新界面进度条的value值。

  而上传或下载的过程中(见上一节的FtpTransfer类的run方法),可以查看,

con.upload(file, newName)方法,代码如下所示,

public int upload(String file, String realName, InputStream in)
{
hasUploaded = true;
Log.out("ftp upload started: " + this);
int stat;
if((in == null) && new File(file).isDirectory())
{
shortProgress = true;
fileCount = 0;
baseFile = file;
dataType = DataConnection.PUTDIR;
isDirUpload = true;
stat = uploadDir(file);
shortProgress = false;
//System.out.println(fileCount + ":" + baseFile);
      fireProgressUpdate(baseFile,
DataConnection.DFINISHED + ":" + fileCount, -1);
fireActionFinished(this);
fireDirectoryUpdate(this);
}
else
    {
dataType = DataConnection.PUT;
stat = rawUpload(file, realName, in);
try
      {
Thread.sleep(100);
}
catch(Exception ex)
{
}
fireActionFinished(this);
fireDirectoryUpdate(this);
}
try
    {
Thread.sleep(500);
}
catch(Exception ex)
{
}
return stat;
}

  

此方法进行负责上传一定字节数量的内容,其实就是调用rawUpload方法,这里没列出,

请参考源代码,而当传完此字节数据后,通过调用fireActionFinished()方法来调用主线程

中的updateProgressBar()方法。其实代码如下:

protected void updateProgressBar() {
int percent = (int) (((float) lFileCompleteSize / (float) lFileSize) * 10000F);
pbFile.setValue(percent);
// System.out.println

("================================================="+percent);
    pbFile.setString(lFileCompleteSize / 1024L + "/" + lFileSize / 1024L
        + " kB");
percent = (int) (((float) lTotalCompleteSize / (float) lTotalSize) * 10000F);
pbTotal.setString(lTotalCompleteSize / 1024L + "/" + lTotalSize / 1024L
        + " kB");
pbTotal.setValue(percent);
repaint();
}

  

上面用了两个进度条,第一个进度条表示当前文件的上传或下载进度,第二个进度条表

示所有文件下载或上传的进度。同时,为了产生进度条的移动或变化进度幅度比较明显,通

过pbFile.setMaximum(10000)及pbTotal.setMaximum(10000)将进度条的最大值设置成10000

,而不是平时我们所设置的100。笔者认为这样比较好看,因为有的时候上传或下载的时候由

于网络原因,可能变化比较小

补充:Jsp教程,Java技巧及代码 
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,