Java 轻量级调用 FTP 上传(安卓可用)
2016-05-10 11:30
423 查看
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 包啦。——然而遗憾的是,在安卓项目中,却无法顺利使用。
怎么说?盖因安卓的 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 更新进度条。
今时今日 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 包啦。——然而遗憾的是,在安卓项目中,却无法顺利使用。
怎么说?盖因安卓的 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 更新进度条。
package ftp; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import sun.net.TelnetInputStream; import sun.net.TelnetOutputStream; import sun.net.ftp.FtpClient2; public class UploadFtp extends FtpClient2 { public UploadFtp(String server, int port) throws IOException { super(server, port); } /** * 用书上传本地文件到ftp服务器上 * * @param source * 上传文件的本地路径 * @param target * 上传到ftp的文件路径 * @return */ public void upload(String source, String target) { TelnetOutputStream ftp = null; InputStream file = null; try { binary(); ftp = put(target); file = new FileInputStream(new File(source)); BufferedInputStream in = new BufferedInputStream(file); new ProgressListener().copy(in, new BufferedOutputStream(ftp), in.available()); System.out.print("put file suc from " + source + " to " + target + "\r\n"); } catch (IOException e) { e.printStackTrace(); } finally { try { if(ftp != null)ftp.close(); if(file != null)file.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 从ftp上下载所需要的文件 * * @param source * 在ftp上路径及文件名 * @param target * 要保存的本地的路径 * @return */ public void getFile(String source, String target) { TelnetInputStream ftp = null; OutputStream file = null; try { binary(); ftp = get(source); file = new FileOutputStream(new File(target)); ProgressListener listener = new ProgressListener(); listener.setFileName(target); listener.copy( new BufferedInputStream(ftp), new BufferedOutputStream(file), getFileSize(source, ftp) ); System.out.print("get file suc from " + source + " to " + target + "\r\n"); } catch (IOException e) { e.printStackTrace(); } finally { try { if(ftp != null)ftp.close(); if(file != null)file.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 为了计算下载速度和百分比,读取ftp该文件的大小 * * @param source * @param ftp * @return * @throws IOException */ private int getFileSize(String source, TelnetInputStream ftp) throws IOException { // 这里的组合使用是必须得 sendServer 后到 readServerResponse sendServer("SIZE " + source + "\r\n"); if (readServerResponse() == 213) { String msg = getResponseString(); try { return Integer.parseInt(msg.substring(3).trim()); } catch (Exception e) {} } return 0; } public static void main(String[] args) { UploadFtp client; try { client = new UploadFtp("192.168.61.83", 21); client.login("upup", "upup@123"); // client.PutFile("D:\\code.jar", "/test/1344439.jar"); client.upload("E:\\aa.mp4", "/temp/zzz.mp4"); client.closeServer(); } catch (IOException e) { e.printStackTrace(); } } }
package ftp; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * 上传进度 * */ public class ProgressListener { private String fileName; private volatile long bytesRead; private volatile long contentLength; public void update(long aBytesRead, long aContentLength) { bytesRead = aBytesRead / 1024L; contentLength = aContentLength / 1024L; // long megaBytes = aBytesRead / 1048576L; System.out.println("上传或者下载文件:" + fileName + ",文件的大小:" + aBytesRead + "/" + aContentLength); } /** * * @param pIn * @param pOut * @return * @throws IOException */ public long copy(InputStream in, OutputStream out, long size) { byte[] buffer = new byte[8192]; long total = 0L; int res; try { while (true) { res = in.read(buffer); if (res == -1) { break; } if (res > 0) { total += res; if (out != null) { out.write(buffer, 0, res); System.out.println("文件的大小" + size + "读取的大小" + total); update(total, size); } } } return total; }catch (IOException e) { return 0L; } finally { try { in.close(); out.close(); } catch (IOException e) { } } } public long getBytesRead() { return this.bytesRead; } public long getContentLength() { return this.contentLength; } public String getFileName() { return fileName; } public void setFileName(String fileName) { this.fileName = fileName; } }使用过程中遇到一个问题,就是不知道为什么不能实例化 FtpClient。但是把 FtpClient 改名字就好了。 *"错误: 在类 sun.net.ftp.FtpClient 中找不到主方法, 请将主方法定义为:public static void main(String[] args)。
相关文章推荐
- java Timer 定时每天凌晨1点执行任务
- java 集合框架-TreeSet练习
- Caused by: java.lang.NoClassDefFoundError: org/aopalliance/intercept/MethodInterceptor
- eclipse提速03 - 禁用动画
- eclipse提速01 - 禁用不常用的eclipse启动插件
- eclipse提速02 - eclipse.ini优化
- java 集合框架-TreeSet
- 关于Java变量的可见性问题
- eclipse怎样对java代码自动排版,快速格式化,快速使代码对齐?
- Java 集合框架-Vector
- PAT-JAVA-5-2 然后是几点 (15分)
- Java 动态代理Proxy和Invacationhandler的使用
- java实现图片上加文字水印(SpringMVC + Jsp)
- PowerDesigner 逆向工程 Could not Initialize JavaVM!
- Java基础(2)变量,循环,数组,方法。。。。。。
- java 集合框架-HashSet
- Java Annotation
- 搭建springMVC报错
- java 区别“==”和equals()方法
- java 集合框架-ArrayList存储不重复的自定义对象