1:使用HTTP协议上传似乎更适合web编程的方便; 上传小于1M的文件比使用FTP协议上传文件稍快。 但对于批量和大文件传输它可能无能为力。 当然,它也有它的优点。 例如,与FTP不同,FTP服务必须在服务器端启动。
2:使用FTP协议上传大于1M的文件比HTTP要快。 文件越大,上传速度比HTTP上传速度快几倍。 并使用java编写程序; FTP 比 HTTP 更方便。
笔者曾经用VB写过控件来上传、下载批处理文件,功能也很强大。 但由于CAB文件或OCX没有专门的数字签名,需要进行繁琐的客户端设置,如设置安全站点、降低客户端的安全级别等,因此一些解决方案被放弃。
同时考虑到需要在客户端对文件进行数字签名和数据加密,我们决定采用该方法来实现。 。 在文件上传之前,客户端可以获取本地密钥信息并完成对上传文件的加密和签名处理。 虽然采用需要在客户端安装JRE运行环境,给客户端的管理和使用带来一些不便,但是相对于如此大量的文件和文件的安全性来说,这或许也算是一个比较小的代价。
总结一下,运行环境是:
FTP服务器:Serv-U,专业的FTP服务器程序。 网上有现成的软件下载。 当然,读者也可以编写一个服务器端FTP文件接收程序来进行说明。 如果没有特殊的需求或者功能,Serv-U应该能够满足我们一般的上传下载需求;
客户端:Java,当年让Java流行起来的技术,据说可以与微软平起平坐。 当然,现在Java已经出来了,是替代品吗?
应用环境:互联网,最终目标。
2、Java FTP客户端库的选择
让我们想象一个情况——我们想编写一个纯Java应用程序,从远程计算机上运行的FTP服务器上传和下载文件; 我们还希望获得远程下载文件的基本文件信息,例如文件名。 、数据或文件大小等。
尽管从头开始编写 FTP 协议处理程序是可能的,而且可能很有趣,但这项工作很困难、漫长,而且有潜在的危险。 因为我们不想花费时间、精力或金钱自己编写这样的处理程序,所以我们转向现有的可重用组件。 而且很多库存都在网上。
找到一个适合我们需求的优秀Java FTP客户端库并不像看起来那么简单。 相反,这是一项非常痛苦和复杂的任务。 首先,寻找FTP客户端库需要一些时间。 其次,当我们找到所有现有的库后,我们应该选择哪一个呢? 每个库都适合不同的需求。 这些库在性能方面并不等同,而且它们的设计也有根本的不同。 每个类库都有自己的特点,并使用不同的术语来描述它们。 因此,评估和比较 FTP 客户端库是一项艰巨的任务。
使用可重用组件是一种值得的方法,但在这种情况下,一开始往往会令人畏惧。 后来我可能会感到有点羞愧:选择了一个好的FTP库之后,后续的工作就会很简单,遵循简单的规则就可以了。 目前有很多公共的、免费的ftp客户端库,比如J-ftp等,其他的还有很多。 如下表所示,表中并未全部列出。 如果读者有更好的客户端FTP库,请进一步添加。
FTP客户端类库名称
评论
J-ftp
.ftp。
工作.ftp..
。
.ftp。
jshop.jnet。
.ftp。
文件传输协议
com.cqs.ftp.FTP
FTP
ftp.dhl.ftp
org..io.ftp。
在本文中,作者使用J-ftp。 这是一个开源且非常强大的客户端 FTP 库。 作者非常喜欢,推荐给各位读者。 算了,免费获得它的广告。
3. 基本功能
1. 登录
使用FTP进行文件传输实际上仍然在使用。 用于沟通。 下面的代码只是类中的登录方法。 当然,在下面的代码中,为了节省篇幅,也为了把一些原理解释清楚,作者去掉了一些不必要的代码,比如日志代码。 完整代码请参考J-ftp的源码或者作者的示例源码。 这同样适用于以下代码示例:
int 登录( , )
这。 = ;
这。 = ;
整数 = ;
jcon = new (主机, 端口);
如果(jcon。())
在 = jcon.();
if(() == null)//) == null)
确定=假;
= ;
如果(!()。())//))
如果(())//))
别的
确定=假;
= ;
别的
如果(消息)
Log.debug("FTP 不可用!");
确定=假;
= ;
如果(好的)
=真;
();
();
[]=新[6];
if(().("OS/2") >= 0)
=“列表”;
如果(列表。(“”))
//只获取第一个项目(它知道第一个是
//FTP列表)
= .(.);
//*** 如果未找到文件,则将其设置为
如果(==空)
列表 = ;
s = new (., 列表);
别的
列表=[0];
如果(列表==空)
列表 = ;
if(().("MVS") >= 0)
列表=“列表”;
//***
(这);
莉兹(这个);
别的
(这个,新的()。());
;
在这个登录方法中,有一个类,它负责建立套接字。 同时这个类是一个独立的线程。 这样做的好处是,为了应对界面的变化,将网络连接等工作都放在单独的线程中处理,有利于界面的友好性。 下面是该类的run方法。 当然这个线程的启动是在类的构造函数中启动的。
无效运行()
尝试
s = new(主机,端口);
= s.();
//if(时间 > 0) s.(时间);
输出=新(新(s.(),
.));
in = new (新(s.()),
.);
确定=真;
// }
抓住(前)
前任。();
Log.out(": 由于 (" + 主机 +
“:”+端口+“)”);
确定=假;
尝试
if((s != null) && !s.())
s.close();
如果(输出!=空)
关闭();
if(in != null)
附寄();
捕获(ex2)
ex2.();
Log.out(": 还有更多要关闭和");
=真;
这里解释一下,在这个run方法中,这个类实现了客户端套接字(也可以称为“套接字”),套接字是两台机器之间的通信端点。 套接字的实际工作是由该类的实例执行的。 应用程序可以通过更改创建套接字实现的套接字工厂来配置自身,以创建适合本地防火墙的套接字。 具体说明请参考JDK5的API说明,最好是中文的。 呵呵。
2.上传下载
文件上传可以分为多线程和单线程。 在单线程的情况下,比较简单,但是在多线程的情况下,需要处理的事情就更多了,需要更加小心。 下面介绍如何上传。 已经考虑了两种不同的类型:单线程和多线程。
int(文件,)
如果(.ing() &&
(!.ading()))
Log.out(" 这是新的。");
t;
如果(!=空)
t = 新(主机、端口、()、()、
文件, , , 。,
, , , crlf);
别的
t = 新(主机、端口、()、()、
文件, , , 。,
, , crlf);
=t;
;
别的
if(.ading())
Log.out("是。");
别的
Log.out("是。");
( == 空) ? (文件):(文件,);
在多线程的情况下,有一个单独的类。 当然,在多线程的情况下,这个类必须是一个单独的线程。 与 类似,它的线程也是在构造方法中启动的。 在其 run 方法中,读取并传输文件。
无效运行()
if(.().get(文件) == null)
.(文件,这个);
否则如果(!暂停)
Log.debug(" in : " + 文件);
工作=假;
统计数据 = 2;
;
= 假;
同时(暂停)
尝试
.sleep(100);
如果(!=空)
for(int i = 0; i < .size(); i++)
(() .(i)).(文件,
,
-1);
如果(!工作)
如果(!=空)
for(int i = 0; i < .size(); i++)
(() .(i)).(文件,
,
-1);
抓住(前)
=真;
while((.() >= .()) &&
(.() > 0) && 工作)
尝试
统计数据 = 4;
.sleep(400);
if(! && ( != null))
for(int i = 0; i < .size(); i++)
(() .(i)).(文件,
,
-1);
别的
休息;
抓住(前)
前任。();
如果(!工作)
如果(!=空)
for(int i = 0; i < .size(); i++)
(() .(i)).(文件,
,
-1);
。(文件);
统计数据 = 3;
;
=真;
尝试
.sleep(.se);
抓住(前)
con = new (主机, 端口, , crlf);
con.();
con.rs();
int = con.login(用户, 通行证);
如果(==。)
文件 f = 新文件();
con.(f.());
如果(类型。())
如果(!=空)
= con.(文件, );
别的
= con.(文件);
别的
= con.(文件,这个.);
如果(!暂停)
。(文件);
至于下载过程,因为是上传的逆过程,所以和上传的方法和写法很相似。 由于篇幅原因,代码就不列出来了,但是思路和思路是完全一样的。 欢迎读者参考源代码。
4.进度条
可以想象,如果上传或下载过程中没有提示,用户无法判断任务是否完成或者任务是否死掉。 这往往会因上传或下载时间过长而误导用户。 因此,进度条非常重要且实用。
进度条的实现其实很简单。 就是在程序中开启两个线程。 第一个线程用于动态更改界面上进度条的值,而第二个线程在上传或下载过程中创建循环。 在这个循环中,每次读取一定量的数据,例如8192字节。 然后传递完这个数据后,调用第一个线程中的方法来更新界面进度条的值。
在上传或下载过程中(参见上一节该类的run方法),可以查看con.(file, )方法。 代码如下,
int(文件,,中)
=真;
Log.out("ftp : " + this);
统计数据;
if((in == null) && 新文件(文件).())
=真;
= 0;
= 文件;
= .;
=真;
统计=(文件);
= 假;
//.out.( + ":" + );
(,
。 +“:”+,-1);
(这);
(这);
别的
= .PUT;
stat = (文件, , 中);
尝试
.sleep(100);
抓住(前)
(这);
(这);
尝试
.sleep(500);
抓住(前)
统计;
该方法负责上传一定数量字节的内容。 其实就是一种调用方法,这里就不列出来了。 请参考源代码。 字节数据传输完毕后,通过调用()方法来调用主线程中的()方法。 方法。 实际上代码如下:
空白 () {
int = (int) (((浮点) / (浮点) ) * );
.();
// .out.("============================================== ======“+”);
.( / 1024L + “/” + / 1024L
+“kB”);
= (int) (((浮点) / (浮点) ) * );
.( / 1024L + “/” + / 1024L
+“kB”);
.();
();
上面使用了两个进度条。 第一进度条指示当前文件的上传或下载进度,第二进度条指示所有文件的下载或上传进度。 同时,为了使进度条的移动或变化更加明显,通过.(10000)和.(10000)将进度条的最大值设置为10000,而不是我们通常设置的100。 笔者觉得这样看起来比较好,因为有时候上传或者下载的时候可能会因为网络的原因变化比较小。 如果设置为100,变化不是特别明显。