Java 轻量级调用 FTP 上传(安卓可用)

Edit:6-19,独立一个标准 Java 项目了,参见在这里的源码

Edit:5-13 更新:为避免与原有 rt.jar 冲突,特意把 sun.* 包名修改,参见在这里的源码

今时今日 HTTP 断点上传、WebSocket 断点上传兴起,FTP 协议大有被取代之势,不过 FTP 顾名思义,始终是为文件传输所服务的,具有 HTTP 所不具备的优势:首先是适合大文件、多批次上传,一般 10MB 甚至上 G 的文件亦可,而且进度条、断点、多线程上传等功能一应俱全;其次开发维护方便,——服务端可以用现成的方案,例如经典的 IIS  FTP、ServU 等,或者写一个服务器端的 FTP 文件接收程序来进行解释,但这样的轮子已经很多了,比如最近的关注 nodejs 也有开源的 FTP 服务方案,所以服务端推荐现成的即可,而且一点就是,服务端一般无需编码而是进行相关设置参数即可;客户端方案,更是有很多选择。姑且不说 Java 领域里面一大堆开源 FTP 项目,其实 Java 类库自带就有 FTP 客户端,只不过并非作为标准库出现,而且蕴含在 rt.jar 的 sun.NET.* 下面;最后我想到 FTP 的优点是自带账号系统,甚至可以对权限进行细分。

笔者最开始使用的是 org.apache.commons.net.ftp.* 包,但相关的例子却找不到上传进度的,而且听说“用 FTPClient(apache)想要强行加入上传文件的速度检测比较麻烦”。令我沮丧,于是唯有试试其他方案。发现 Java 开源世界仍旧精彩,各种 FTP Client 比比皆是,


表格出处《用Java实现FTP批量大文件上传下载》

这么多怎么选择呢?如果是一般简单的上传下载,犯不着引入臃肿第三方库,使用 Sun 出品的 sun.Net.ftp.FtpClient 即可满足。笔者试过 FtpClient 后,可以加入获取进度的功能,于是就可以抛弃 200多 kb 的  Apache FTP 包啦。——然而遗憾的是,在安卓项目中,Sun FTP 却无法顺利使用。

怎么说?盖因安卓的 JDK 自行体系,是没有加入 rt.jar 这样非标准库的包(而 Web 环境则无压力)。我百度了一下,虽然可以提供部分 FtpClient 源码但不是完整的。好在 Java 就是开源的,——转战谷歌搜索立马就搜到 JDK 的完整代码。开始的时候是 CSDN 下载的,不过够奇葩的是,


想想 Java 布尔类型可以用 0/1 表示那是多的老版本呀,故所以接着各种报错就在所难免。

于是再找新版本的的 JDK,——OpenJDK 1.7,这下可以有了,最新源码,注释也有,可是就是进化得“太先进”了,包分的太细,依赖 N 个类,抽出来太费力了。怎么办?退而求其次,来个 1.6 看怎么样,果然 1.6 的符合刚好够用的原则,主要是以下几个类。

下面是相关 API 说明。

sun.net.ftp.FtpClient 该类库主要提供了用于建立FTP连接的类。利用这些类的方法,编程人员可以远程登录到FTP服务器,列举该服务器上的目录,设置传输协议,以及传送文件。FtpClient类涵盖了几乎所有FTP的功能,FtpClient的实例变量保存了有关建立"代理"的各种信息。

public static boolean useFtpProxy
  这个变量用于表明FTP传输过程中是否使用了一个代理,因此,它实际上是一个标记,此标记若为TRUE,表明使用了一个代理主机。

  public static String ftpProxyHost   此变量只有在变量useFtpProxy为TRUE时才有效,用于保存代理主机名。

 

  public static int ftpProxyPort   此变量只有在变量useFtpProxy为TRUE时才有效,用于保存代理主机的端口地址。

 

  FtpClient有三种不同形式的构造函数,如下所示:

  1、public FtpClient(String hostname,int port)    此构造函数利用给出的主机名和端口号建立一条FTP连接。

 

  2、public FtpClient(String hostname)
  此构造函数利用给出的主机名建立一条FTP连接,使用默认端口号。

 

  3、FtpClient()
  此构造函数将创建一FtpClient类,但不建立FTP连接。这时,FTP连接可以用openServer方法建立。

  一旦建立了类FtpClient,就可以用这个类的方法来打开与FTP服务器的连接。类ftpClient提供了如下两个可用于打开与FTP服务器之间的连接的方法。

  public void openServer(String hostname)
  这个方法用于建立一条与指定主机上的FTP服务器的连接,使用默认端口号。

  public void openServer(String host,int port)
  这个方法用于建立一条与指定主机、指定端口上的FTP服务器的连接。

 

  打开连接之后,接下来的工作是注册到FTP服务器。这时需要利用下面的方法。

  public void login(String username,String password)
  此方法利用参数username和password登录到FTP服务器。使用过Intemet的用户应该知道,匿名FTP服务器的登录用户名为anonymous,密码一般用自己的电子邮件地址。

 

  下面是FtpClient类所提供的一些控制命令。

  public void cd(String remoteDirectory):该命令用于把远程系统上的目录切换到参数remoteDirectory所指定的目录。
  public void cdUp():该命令用于把远程系统上的目录切换到上一级目录。
  public String pwd():该命令可显示远程系统上的目录状态。
  public void binary():该命令可把传输格式设置为二进制格式。
  public void ascii():该命令可把传输协议设置为ASCII码格式。
  public void rename(String string,String string1):该命令可对远程系统上的目录或者文件进行重命名操作。

 

  除了上述方法外,类FtpClient还提供了可用于传递并检索目录清单和文件的若干方法。这些方法返回的是可供读或写的输入、输出流。下面是其中一些主要的方法。

  public TelnetInputStream list()
  返回与远程机器上当前目录相对应的输入流。

 

  public TelnetInputStream get(String filename)
  获取远程机器上的文件filename,借助TelnetInputStream把该文件传送到本地。

 

  public TelnetOutputStream put(String filename)
  以写方式打开一输出流,通过这一输出流把文件filename传送到远程计算机。

至此,我们已经把 sun.net.ftp 的包单独抽取出来,安卓也可用使用了。

下面是调用的例子,主要是 UploadFtp 工具类,继承 FtpClient。UploadProgressListener 是上传进度条控制器,你可以修改其中的 update() 通知 UI 更新进度条。

[java]  view plain  copy
  1. package ftp;  
  2.   
  3. import java.io.BufferedInputStream;  
  4. import java.io.BufferedOutputStream;  
  5. import java.io.File;  
  6. import java.io.FileInputStream;  
  7. import java.io.FileOutputStream;  
  8. import java.io.IOException;  
  9. import java.io.InputStream;  
  10. import java.io.OutputStream;  
  11.   
  12. import sun.net.TelnetInputStream;  
  13. import sun.net.TelnetOutputStream;  
  14. import sun.net.ftp.FtpClient2;  
  15.   
  16. public class UploadFtp extends FtpClient2 {  
  17.     public UploadFtp(String server, int port) throws IOException  {  
  18.         super(server, port);  
  19.     }  
  20.   
  21.     /** 
  22.      * 用书上传本地文件到ftp服务器上 
  23.      *  
  24.      * @param source 
  25.      *            上传文件的本地路径 
  26.      * @param target 
  27.      *            上传到ftp的文件路径 
  28.      * @return 
  29.      */  
  30.     public void upload(String source, String target) {  
  31.         TelnetOutputStream ftp = null;  
  32.         InputStream file = null;  
  33.           
  34.         try {  
  35.             binary();  
  36.               
  37.             ftp = put(target);  
  38.             file = new FileInputStream(new File(source));  
  39.             BufferedInputStream in = new BufferedInputStream(file);  
  40.               
  41.             new ProgressListener().copy(in, new BufferedOutputStream(ftp), in.available());  
  42.               
  43.             System.out.print("put file suc from " + source + "   to  " + target + "\r\n");  
  44.         } catch (IOException e) {  
  45.             e.printStackTrace();  
  46.         } finally {  
  47.             try {  
  48.                 if(ftp != null)ftp.close();  
  49.                 if(file != null)file.close();  
  50.             } catch (IOException e) {  
  51.                 e.printStackTrace();  
  52.             }  
  53.         }  
  54.     }  
  55.   
  56.     /** 
  57.      * 从ftp上下载所需要的文件 
  58.      *  
  59.      * @param source 
  60.      *            在ftp上路径及文件名 
  61.      * @param target 
  62.      *            要保存的本地的路径 
  63.      * @return 
  64.      */  
  65.     public void getFile(String source, String target) {  
  66.         TelnetInputStream ftp = null;  
  67.         OutputStream file = null;  
  68.           
  69.         try {  
  70.             binary();  
  71.             ftp = get(source);  
  72.             file = new FileOutputStream(new File(target));  
  73.               
  74.             ProgressListener listener = new ProgressListener();  
  75.             listener.setFileName(target);  
  76.             listener.copy(  
  77.                 new BufferedInputStream(ftp),   
  78.                 new BufferedOutputStream(file),   
  79.                 getFileSize(source, ftp)  
  80.             );  
  81.       
  82.             System.out.print("get file suc from " + source + "   to  " + target + "\r\n");  
  83.         } catch (IOException e) {  
  84.             e.printStackTrace();  
  85.         } finally {  
  86.             try {  
  87.                 if(ftp != null)ftp.close();  
  88.                 if(file != null)file.close();  
  89.             } catch (IOException e) {  
  90.                 e.printStackTrace();  
  91.             }  
  92.         }  
  93.     }  
  94.   
  95.     /** 
  96.      * 为了计算下载速度和百分比,读取ftp该文件的大小 
  97.      *  
  98.      * @param source 
  99.      * @param ftp 
  100.      * @return 
  101.      * @throws IOException 
  102.      */  
  103.     private int getFileSize(String source, TelnetInputStream ftp) throws IOException {  
  104.         // 这里的组合使用是必须得 sendServer 后到 readServerResponse  
  105.         sendServer("SIZE " + source + "\r\n");  
  106.           
  107.         if (readServerResponse() == 213) {  
  108.             String msg = getResponseString();  
  109.               
  110.             try {  
  111.                 return Integer.parseInt(msg.substring(3).trim());  
  112.             } catch (Exception e) {}  
  113.         }  
  114.           
  115.         return 0;  
  116.     }  
  117.   
  118.     public static void main(String[] args)  {  
  119.         UploadFtp client;  
  120.         try {  
  121.             client = new UploadFtp("192.168.61.83"21);  
  122.             client.login("upup""upup@123");  
  123. //            client.PutFile("D:\\code.jar", "/test/1344439.jar");  
  124.             client.upload("E:\\aa.mp4""/temp/zzz.mp4");  
  125.             client.closeServer();  
  126.         } catch (IOException e) {  
  127.             e.printStackTrace();  
  128.         }  
  129.   
  130.     }  
  131. }  

[java]  view plain  copy
  1. package ftp;  
  2.   
  3. import java.io.IOException;  
  4. import java.io.InputStream;  
  5. import java.io.OutputStream;  
  6.   
  7. /** 
  8.  * 上传进度 
  9.  * 
  10.  */  
  11. public class ProgressListener {  
  12.     private String fileName;  
  13.     private volatile long bytesRead;  
  14.     private volatile long contentLength;  
  15.   
  16.     public void update(long aBytesRead, long aContentLength) {  
  17.         bytesRead = aBytesRead / 1024L;  
  18.         contentLength = aContentLength / 1024L;  
  19.   
  20.         // long megaBytes = aBytesRead / 1048576L;  
  21.   
  22.         System.out.println("上传或者下载文件:" + fileName + ",文件的大小:" + aBytesRead + "/" + aContentLength);  
  23.     }  
  24.   
  25.     /** 
  26.      *  
  27.      * @param pIn 
  28.      * @param pOut 
  29.      * @return 
  30.      * @throws IOException 
  31.      */  
  32.     public long copy(InputStream in, OutputStream out, long size) {  
  33.         byte[] buffer = new byte[8192];  
  34.   
  35.         long total = 0L;  
  36.         int res;  
  37.   
  38.         try {  
  39.             while (true) {  
  40.                 res = in.read(buffer);  
  41.                 if (res == -1) {  
  42.                     break;  
  43.                 }  
  44.                 if (res > 0) {  
  45.                     total += res;  
  46.                     if (out != null) {  
  47.                         out.write(buffer, 0, res);  
  48.                         System.out.println("文件的大小" + size + "读取的大小" + total);  
  49.                         update(total, size);  
  50.                     }  
  51.                 }  
  52.             }  
  53.                   
  54.             return total;  
  55.         }catch (IOException e) {  
  56.             return 0L;  
  57.         }  finally {  
  58.             try {  
  59.                 in.close();  
  60.                 out.close();  
  61.             } catch (IOException e) {  
  62.             }  
  63.         }  
  64.     }  
  65.   
  66.     public long getBytesRead() {  
  67.         return this.bytesRead;  
  68.     }  
  69.   
  70.     public long getContentLength() {  
  71.         return this.contentLength;  
  72.     }  
  73.   
  74.     public String getFileName() {  
  75.         return fileName;  
  76.     }  
  77.   
  78.     public void setFileName(String fileName) {  
  79.         this.fileName = fileName;  
  80.     }  
  81. }  

使用过程中遇到一个问题,就是不知道为什么不能实例化 FtpClient。但是把 FtpClient 改名字就好了。 *"错误: 在类 sun.net.ftp.FtpClient 中找不到主方法, 请将主方法定义为:public static void main(String[] args)。


原文链接:http://blog.csdn.net/zhangxin09/article/details/51360874

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值