Java语言实现简单FTP软件
2016-01-29 20:40
639 查看
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!
转载请保留原文地址:/article/1341360.html
一、FTP协议分析
FTP(File Transfer Protocol)就是文件传输协议。通过FTP客户端从远程FTP服务器上拷贝文件到本地计算机称为下载,将本地计算机上的文件复制到远程FTP服务器上称为上传,上传和下载是FTP最常用的两个功能。FTP使用传输层的TCP协议进行传输,因此客户端与服务器之间的连接是可靠的,而且是面向连接,为数据的传输提供了可靠的保证。FTP的目标有以下目标:
提高文件的共享性
提供非直接地远程操纵计算机
避免用户因主机之间的文件存储系统的差异而导致的变化
为数据的传送提供可靠性和高效性
FTP协议模型如下图所示
![](http://img.blog.csdn.net/20130730215535906?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
FTP使用TCP的服务,它需要两条连接。一条是数据连接用于数据传送,一般使用端口21,而另一条是控制连接用于传送控制信息(命令和响应),一般使用端口20。控制连接需要传送的只是控制信息,如一行命令或一行应答码,而数据连接需要传送的数据类型繁杂,如文本文件、图形文件、应用程序等等。
FTP协议模型中使用到的交互元素包括用户接口、USERPI、UPTP、SPI、SDTP的说明如下图所示
![](http://img.blog.csdn.net/20130730215717843?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
FTP传输有两种方式:文本传输模式和二进制数据传输模式。其中文本模式又叫ASCII模式,二进制模式又叫Binary模式。
FTP服务分为普通FTP与匿名FTP服务两种类型。
常用FTP的命令有:
1) 接入命令
USER: 指明用户名
PASS: 指明与用户名对应的密码
ABOR: 异常中断数据连接程序
QUIT: 从系统注销
REIN: 重新初始化
2) 文件管理命令
CWD: 改变服务器上的工作目录到指定目录
CDUP: 改变服务器上的工作目录到父目录
DELE: 请求删除服务器上的文件。
LIST: 列出子目录或文件
MKD: 请求在服务器上新建一个目录
PWD: 显示当前工作目录
RMD: 从服务器上删除指定目录
3) 数据格式化命令
TYPE: 定义文件类型,共有四种类型,所带参数也有四种:A、E 、I 、L分别对应ASCII,EBCDIC,IMAGB和LOCAL类型。
STRU: 定义数据的组织
MODE: 定义传输方式
4) 端口定义命令
PASV:服务器选择端口,客户端使用这个端口发送主动打开
PORT: 客户端选择端口,服务器使用这个端口创建主动打开
5) 文件传送命令
RETR: 读取文件,文件从服务器端传送到客户端
STOR: 存放文件,文件从客户端传送到服务器端
STAT: 返回文件的状态
ALLOO: 在服务器为文件分配存储空间
6) 杂项命令
HELP: 询问服务器的信息
NOOP: 检查服务器是否工作
SITE: 指定特定场所的命令
SYST: 询问服务器使用的操作系统
客户端发送 FTP 命令后,服务器返回响应码。响应码用三位数字编码表示:
第一个数字定义命令的状态。
1 表示服务器正确接收信息,还未处理。
2 表示服务器已经正确处理信息。
3 表示服务器正确接收信息,正在处理。
4 表示信息暂时错误。
5 表示信息永久错误。
第二个数字是响应类型的分类。
0 表示语法。
1 表示系统状态和信息。
2 表示连接状态。
3 表示与用户认证有关的信息。
4 表示未指明。
5 表示与文件系统有关的信息。
第三个数字提供了更加附加信息。
二、FTP软件效果图预览之下载功能
介绍完FTP协议后,来看看该软件完成后的效果图
![](http://img.blog.csdn.net/20130730221427515?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
客户端的主界面如上图所示,主要是分为以下几个界面:
数据输入界面:用来让用户输入服务器的地址,用户名,密码,端口号等。
站点菜单、本地菜单、远程菜单以及帮助菜单。
本地文件信息显示界面:主要是用来显示本地文件列表以及文件详情,以及对文件进行相关操作。
远程文件信息显示界面:主要是用来显示远程FTP服务器端文件列表以及文件详情,以及对文件进行相关操作。
上传下载队列显示界面:显示正在下载或者上传的文件线程的进度。
![](http://img.blog.csdn.net/20130730221808343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2、下载中,进度条显示
![](http://img.blog.csdn.net/20130730222153125?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
3.下载完成
![](http://img.blog.csdn.net/20130730222811515?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
三、FTP软件效果图预览之上传功能
下面展示一下上传功能的过程
1、上传前
上传前选择好要将文件或文件夹上传到远程FTP服务器的哪个目的目录下。
![](http://img.blog.csdn.net/20130730224938531?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2、上传中
添加上传任务
![](http://img.blog.csdn.net/20130730225123171?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
上传任务完成进度显示
![](http://img.blog.csdn.net/20130730225222546?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
3、上传完成
![](http://img.blog.csdn.net/20130730225435171?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
四、FTP软件主界面的实现
首先看一下该软件的整体代码框架
![](http://img.blog.csdn.net/20130731211501046?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20130731211544781?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1、首先介绍程序的主入口FTPMain.java,采用了一个漂亮的外观风格
整体界面如下:
![](http://img.blog.csdn.net/20130731211841781?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
五、FTP软件本地窗口的实现
1、首先看一下本地窗口的布局效果
![](http://img.blog.csdn.net/20130731212523562?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2、看一下本地窗口实现的代码框架
![](http://img.blog.csdn.net/20130731212834406?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2、本地窗口的具体实现代码LocalPanel.java
1、首先看一下远程窗口的布局效果
![](http://img.blog.csdn.net/20130731213314296?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2、看一下本地窗口实现的代码框架
![](http://img.blog.csdn.net/20130731213428312?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
3、远程窗口主要实现代码FtpPanel.java
1、首先看一下队列窗口的界面
![](http://img.blog.csdn.net/20130805173813125?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2、看一下上传队列窗口的界面
![](http://img.blog.csdn.net/20130805173824812?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
3、看一下下载队列窗口的界面
![](http://img.blog.csdn.net/20130805173837203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
4.队列窗口的实现
1.上传任务的添加
![](http://img.blog.csdn.net/20130805174832203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
2.上传任务的完成
![](http://img.blog.csdn.net/20130805174908281?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
3.下载任务的添加
![](http://img.blog.csdn.net/20130805174925562?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
4.下载任务的完成
![](http://img.blog.csdn.net/20130805174944000?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
八、连接管理模块的实现:主机与服务器之间的连接与关闭操作
(1)FTP连接
运行FTP客户端后,首先是连接FTP服务器,需要输入FTP服务器的IP地址及用户名、密码以及端口号后点击连接按钮开始连接FTP服务器,连接流程图如下图所示。
![](http://img.blog.csdn.net/20130813193513453?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
点击“连接”按钮后,会调用com.oyp.ftp.FTPClientFrame类的linkButtonActionPerformed(ActionEvent evt)方法,其主要代码程序如下
(2)FTP断开
点击“断开”按钮,会停止上传线程,停止下载线程,清空任务队列,清除FTP资源表格内容,清除本地面板的队列等,断开端连接模块流程图如图所示。
![](http://img.blog.csdn.net/20130813193603343?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
点击“断开”按钮,会触发com.oyp.ftp.CutLinkAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
首先看一下界面:
![](http://img.blog.csdn.net/20130813200149625?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1、本地文件列表的显示功能
将本地的当前目录下所有文件显示出来,并显示文件的属性包括文件名、大小、日期、通过javax.swing.JTable()来显示具体的数据。更改当前文件目录会调用com.oyp.ftp.panel.local.LocalPanel类的listLocalFiles()方法,其主要代码如下
2、本地文件列表的刷新功能
点击“刷新”按钮,会触发com.oyp.ftp.panel.local.RefreshAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
3、 新建本地文件夹功能
点击“新建文件夹”按钮,会触发com.oyp.ftp.panel.local.CreateFolderAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,填写要新建的文件夹名称,选择“确定”,“取消”按钮结束。其主要代码如下
4、删除本地文件功能
选择好要删除的文件或文件夹,点击“删除”按钮,会触发com.oyp.ftp.panel.local.DelFileAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,选择“是”,“否”,“取消”按钮结束。其主要代码如下
5、重命名本地文件功能
选择好要重命名的文件或文件夹,点击“重命名”按钮,会触发com.oyp.ftp.panel.local.RennameAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
首先看一下界面:
![](http://img.blog.csdn.net/20130813200740734?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1、远程FTP服务器端的文件列表的显示
将远程的当前目录下所有文件显示出来,并显示文件的属性包括文件名、大小、日期、通过javax.swing.JTable()来显示具体的数据。更改当前文件目录会调用com.oyp.ftp.panel.ftp.FtpPanel类的listFtpFiles(final TelnetInputStream list)方法,其主要代码如下
2、刷新远程FTP服务器端的文件列表
点击“刷新”按钮,会触发com.oyp.ftp.panel.ftp.RefreshAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
点击“新建文件夹”按钮,会触发com.oyp.ftp.panel.ftp.CreateFolderAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,填写要新建的文件夹名称,选择“确定”,“取消”按钮结束。其主要代码如下
选择好要删除的文件或文件夹,点击“删除”按钮,会触发com.oyp.ftp.panel.ftp.DelFileAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,选择“是”,“否”,“取消”按钮结束。其主要代码如下
选择好要重命名的文件或文件夹,点击“重命名”按钮,会触发com.oyp.ftp.panel.ftp.RenameAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
1、上传本地文件或文件夹到远程FTP服务器端的功能。
当用户在本地文件列表中选择想要上传的文件后,点击上传按钮,将本机上指定的文件上传到FTP服务器当前展现的目录,下图为上传子模块流程图
![](http://img.blog.csdn.net/20130814200757843?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
选择好要上传的文件或文件夹,点击“上传”按钮,会触发com.oyp.ftp.panel.local.UploadAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
2、下载远程FTP服务器端的文件或文件夹到本地
当用户在远程FTP服务器文件列表中选择想要下载的文件后,点击下载按钮,将服务器上的文件下载至本机,下图为下载子模块流程图。
![](http://img.blog.csdn.net/20130814201533468?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
选择好要下载的文件或文件夹,点击“下载”按钮,会触发com.oyp.ftp.panel.ftp.DownAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
1、FTP站点管理
点击“FTP站点管理”按钮,弹出对话框“FTP站点管理”,如下图
![](http://img.blog.csdn.net/20130814202348921?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
1) 连接站点
在FTP站点管理面板上选好要连接的站点,点击“连接”按钮,则会将主机地址、端口号、用户名好,并将密码清空,如下图
![](http://img.blog.csdn.net/20130814202440265?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
到其主要代码如下
2) 添加站点
在FTP站点管理面板上点击“添加”按钮,会产生一个新的对话框“添加FTP站点”,如下图
![](http://img.blog.csdn.net/20130814202638687?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
填写好站点名称、地址、端口号、登陆用户后点击”确定”或者”重置”按钮会触发com.oyp.ftp.panel.manager.SiteDialog类的actionPerformed(ActionEvent e)方法,其代码如下
3) 编辑站点
在FTP站点管理面板上选好要编辑的站点,点击“编辑”按钮,会产生一个新的对话框“编辑FTP站点”,如下图
![](http://img.blog.csdn.net/20130814202921281?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
编辑写好站点名称、地址、端口号、登陆用户后点击”确定”或者”重置”按钮触发com.oyp.ftp.panel.manager.SiteDialog类的actionPerformed(ActionEvent e)方法,其代码如添加站点里一样。
4) 删除站点
在FTP站点管理面板上选好要删除的站点,点击“删除”按钮,调用delSite(SiteInfoBean bean)方法,其代码如下
2、上传/下载任务结束后自动关机
在com.oyp.ftp.panel.queue.QueuePanel类的refreshQueue()方法里会判断任务队列是否为空以及自动关机按钮是否被按下,如果满足条件则执行系统关机命令,延迟30秒后自动关机。其代码如下
3、软件系统化托盘
当点击最小化软件后,系统就会变成一个生成系统推盘,点击系统托盘右键会有“显示主窗体”和“退出”两个菜单,如下图
![](http://img.blog.csdn.net/20130814203212828?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20130814203223218?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvb3V5YW5nX3Blbmc=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
以下是初始化系统托盘的代码,如下
http://download.csdn.net/detail/u013510614/9422600
配套服务器地址:
http://download.csdn.net/detail/u013510614/9422583
转载请保留原文地址:/article/1341360.html
一、FTP协议分析
FTP(File Transfer Protocol)就是文件传输协议。通过FTP客户端从远程FTP服务器上拷贝文件到本地计算机称为下载,将本地计算机上的文件复制到远程FTP服务器上称为上传,上传和下载是FTP最常用的两个功能。FTP使用传输层的TCP协议进行传输,因此客户端与服务器之间的连接是可靠的,而且是面向连接,为数据的传输提供了可靠的保证。FTP的目标有以下目标:
提高文件的共享性
提供非直接地远程操纵计算机
避免用户因主机之间的文件存储系统的差异而导致的变化
为数据的传送提供可靠性和高效性
FTP协议模型如下图所示
FTP使用TCP的服务,它需要两条连接。一条是数据连接用于数据传送,一般使用端口21,而另一条是控制连接用于传送控制信息(命令和响应),一般使用端口20。控制连接需要传送的只是控制信息,如一行命令或一行应答码,而数据连接需要传送的数据类型繁杂,如文本文件、图形文件、应用程序等等。
FTP协议模型中使用到的交互元素包括用户接口、USERPI、UPTP、SPI、SDTP的说明如下图所示
FTP传输有两种方式:文本传输模式和二进制数据传输模式。其中文本模式又叫ASCII模式,二进制模式又叫Binary模式。
FTP服务分为普通FTP与匿名FTP服务两种类型。
常用FTP的命令有:
1) 接入命令
USER: 指明用户名
PASS: 指明与用户名对应的密码
ABOR: 异常中断数据连接程序
QUIT: 从系统注销
REIN: 重新初始化
2) 文件管理命令
CWD: 改变服务器上的工作目录到指定目录
CDUP: 改变服务器上的工作目录到父目录
DELE: 请求删除服务器上的文件。
LIST: 列出子目录或文件
MKD: 请求在服务器上新建一个目录
PWD: 显示当前工作目录
RMD: 从服务器上删除指定目录
3) 数据格式化命令
TYPE: 定义文件类型,共有四种类型,所带参数也有四种:A、E 、I 、L分别对应ASCII,EBCDIC,IMAGB和LOCAL类型。
STRU: 定义数据的组织
MODE: 定义传输方式
4) 端口定义命令
PASV:服务器选择端口,客户端使用这个端口发送主动打开
PORT: 客户端选择端口,服务器使用这个端口创建主动打开
5) 文件传送命令
RETR: 读取文件,文件从服务器端传送到客户端
STOR: 存放文件,文件从客户端传送到服务器端
STAT: 返回文件的状态
ALLOO: 在服务器为文件分配存储空间
6) 杂项命令
HELP: 询问服务器的信息
NOOP: 检查服务器是否工作
SITE: 指定特定场所的命令
SYST: 询问服务器使用的操作系统
客户端发送 FTP 命令后,服务器返回响应码。响应码用三位数字编码表示:
第一个数字定义命令的状态。
1 表示服务器正确接收信息,还未处理。
2 表示服务器已经正确处理信息。
3 表示服务器正确接收信息,正在处理。
4 表示信息暂时错误。
5 表示信息永久错误。
第二个数字是响应类型的分类。
0 表示语法。
1 表示系统状态和信息。
2 表示连接状态。
3 表示与用户认证有关的信息。
4 表示未指明。
5 表示与文件系统有关的信息。
第三个数字提供了更加附加信息。
二、FTP软件效果图预览之下载功能
介绍完FTP协议后,来看看该软件完成后的效果图
客户端的主界面如上图所示,主要是分为以下几个界面:
数据输入界面:用来让用户输入服务器的地址,用户名,密码,端口号等。
站点菜单、本地菜单、远程菜单以及帮助菜单。
本地文件信息显示界面:主要是用来显示本地文件列表以及文件详情,以及对文件进行相关操作。
远程文件信息显示界面:主要是用来显示远程FTP服务器端文件列表以及文件详情,以及对文件进行相关操作。
上传下载队列显示界面:显示正在下载或者上传的文件线程的进度。
下载测试
1、下载前选好要下载到的目的目录以及要下载的文件2、下载中,进度条显示
3.下载完成
三、FTP软件效果图预览之上传功能
下面展示一下上传功能的过程
1、上传前
上传前选择好要将文件或文件夹上传到远程FTP服务器的哪个目的目录下。
2、上传中
添加上传任务
上传任务完成进度显示
3、上传完成
四、FTP软件主界面的实现
首先看一下该软件的整体代码框架
1、首先介绍程序的主入口FTPMain.java,采用了一个漂亮的外观风格
package com.oyp.ftp; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.UIManager; import com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel; public class FTPMain { /** * 本应用的程序入口 */ public static void main(String args[]) { //导致 runnable 的 run 方法在 EventQueue 的指派线程上被调用。 java.awt.EventQueue.invokeLater(new Runnable() { public void run() { try { //使用 LookAndFeel 对象设置当前的默认外观。 UIManager.setLookAndFeel(new NimbusLookAndFeel());//设置一个非常漂亮的外观 // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); FTPClientFrame client_Frame = new FTPClientFrame(); client_Frame.setVisible(true); } catch (Exception ex) { Logger.getLogger(FTPClientFrame.class.getName()).log( Level.SEVERE, null, ex); } } }); } }2、介绍界面的主程序代码FTPClientFrame.java
package com.oyp.ftp; import java.awt.AWTException; import java.awt.MenuItem; import java.awt.PopupMenu; import java.awt.SystemTray; import java.awt.TrayIcon; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowEvent; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPasswordField; import javax.swing.JSeparator; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.UIManager; import com.oyp.ftp.panel.ftp.FtpPanel; import com.oyp.ftp.panel.local.LocalPanel; import com.oyp.ftp.panel.manager.FtpSiteDialog; import com.oyp.ftp.panel.queue.DownloadPanel; import com.oyp.ftp.panel.queue.QueuePanel; import com.oyp.ftp.panel.queue.UploadPanel; import com.oyp.ftp.utils.FtpClient; import com.oyp.ftp.utils.SiteInfoBean; import com.sun.java.swing.plaf.nimbus.*; public class FTPClientFrame extends javax.swing.JFrame { FtpClient ftpClient; private JPasswordField PassField; private JButton cutLinkButton; FtpPanel ftpPanel; LocalPanel localPanel; private JTextField portTextField; private JTextField serverTextField; private JTextField userTextField; private QueuePanel queuePanel; private UploadPanel uploadPanel; private DownloadPanel downloadPanel; private JSplitPane jSplitPane1; private JButton linkButton; private final LinkToAction LINK_TO_ACTION; // 连接到 按钮的动作处理器 private final CutLinkAction CUT_LINK_ACTION; // 断开 按钮的动作处理器 private SystemTray systemTray; private JToggleButton shutdownButton; private final ImageIcon icon = new ImageIcon(getClass().getResource( "/com/oyp/ftp/res/trayIcon.png")); public FTPClientFrame() { LINK_TO_ACTION = new LinkToAction(this, "连接到", null); CUT_LINK_ACTION = new CutLinkAction(this, "断开", null); initComponents(); initSystemTray(); } /** * 初始化系统托盘的方法 */ private void initSystemTray() { if (SystemTray.isSupported()) systemTray = SystemTray.getSystemTray(); TrayIcon trayIcon = new TrayIcon(icon.getImage()); PopupMenu popupMenu = new PopupMenu("托盘菜单"); // 创建显示主窗体菜单项 MenuItem showMenuItem = new MenuItem("显示主窗体"); showMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { FTPClientFrame.this.setExtendedState(JFrame.NORMAL); FTPClientFrame.this.setVisible(true); } }); // 创建退出菜单项 MenuItem exitMenuItem = new MenuItem("退出"); exitMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); popupMenu.add(showMenuItem); popupMenu.addSeparator(); popupMenu.add(exitMenuItem); trayIcon.setPopupMenu(popupMenu); try { systemTray.add(trayIcon); } catch (AWTException e) { e.printStackTrace(); } } /** * 初始化程序界面的方法 */ private void initComponents() { setIconImage(icon.getImage()); java.awt.GridBagConstraints gridBagConstraints; JPanel jPanel1 = new JPanel(); JToolBar jToolBar1 = new JToolBar(); JButton linkTo = new JButton(); cutLinkButton = new JButton(); JPanel jPanel4 = new JPanel(); JLabel jLabel1 = new JLabel(); serverTextField = new JTextField(); JLabel jLabel2 = new JLabel(); userTextField = new JTextField(); JLabel jLabel3 = new JLabel(); PassField = new JPasswordField(); JLabel jLabel6 = new JLabel(); portTextField = new JTextField(); linkButton = new JButton(); JSplitPane jSplitPane2 = new JSplitPane(); jSplitPane1 = new JSplitPane(); ftpPanel = new FtpPanel(this); // 初始化FTP远程资源面板 localPanel = new LocalPanel(this); // 初始化本地资源管理面板 uploadPanel = new UploadPanel(); // 初始化上传队列面板 downloadPanel = new DownloadPanel(); // 初始化下载队列面板 queuePanel = new QueuePanel(this); // 初始化队列面板 JTabbedPane jTabbedPane1 = new JTabbedPane(); JMenuBar MenuBar = new JMenuBar(); JMenu fileMenu = new JMenu(); JMenuItem ftpManageMenuItem = new JMenuItem(); JSeparator jSeparator1 = new JSeparator(); JMenuItem linkToMenuItem = new javax.swing.JMenuItem(); JMenuItem cutMenuItem = new javax.swing.JMenuItem(); JSeparator jSeparator2 = new javax.swing.JSeparator(); JMenuItem exitMenuItem = new javax.swing.JMenuItem(); JMenuItem uploadMenuItem = new javax.swing.JMenuItem(); JSeparator jSeparator3 = new javax.swing.JSeparator(); JMenuItem createFolderMenuItem = new javax.swing.JMenuItem(); JMenuItem renameMenuItem = new javax.swing.JMenuItem(); JMenuItem delMenuItem = new javax.swing.JMenuItem(); JMenu ftpMenu = new javax.swing.JMenu(); JMenuItem downMenuItem = new javax.swing.JMenuItem(); JSeparator jSeparator6 = new javax.swing.JSeparator(); JMenuItem ftpDelMenuItem = new javax.swing.JMenuItem(); JMenuItem ftpRenameMenuItem = new javax.swing.JMenuItem(); JMenuItem newFolderMenuItem = new javax.swing.JMenuItem(); JMenu helpMenu = new javax.swing.JMenu(); JMenuItem aboutMenuItem = new javax.swing.JMenuItem(); JMenuItem bugMenuItem = new javax.swing.JMenuItem(); // setTitle("基于Socket的FTP软件Java实现"); setTitle("Java语言实现简单FTP软件__欧阳鹏设计"); addWindowListener(new java.awt.event.WindowAdapter() { public void windowOpened(java.awt.event.WindowEvent evt) { formWindowOpened(evt); } public void windowIconified(final WindowEvent e) { setVisible(false); } }); addComponentListener(new java.awt.event.ComponentAdapter() { public void componentResized(java.awt.event.ComponentEvent evt) { formComponentResized(evt); } }); getContentPane().setLayout(new java.awt.GridBagLayout()); jPanel1.setLayout(new java.awt.GridLayout(0, 1)); jToolBar1.setRollover(true); jToolBar1.setFloatable(false); linkTo.setText("连接到"); linkTo.setFocusable(false); linkTo.setAction(LINK_TO_ACTION); jToolBar1.add(linkTo); cutLinkButton.setText("断开"); cutLinkButton.setEnabled(false); cutLinkButton.setFocusable(false); cutLinkButton.setAction(CUT_LINK_ACTION); jToolBar1.add(cutLinkButton); jPanel1.add(jToolBar1); shutdownButton = new JToggleButton(); shutdownButton.setText("自动关机"); shutdownButton.setToolTipText("队列完成后,自动关闭计算机"); shutdownButton.setFocusable(false); jToolBar1.add(shutdownButton); jPanel4.setBorder(javax.swing.BorderFactory.createEtchedBorder()); jPanel4.setLayout(new javax.swing.BoxLayout(jPanel4, javax.swing.BoxLayout.LINE_AXIS)); jLabel1.setText("主机地址:"); jPanel4.add(jLabel1); serverTextField.setText("192.168.1.100"); serverTextField.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(java.awt.event.KeyEvent evt) { LinkFTPKeyPressed(evt); } }); jPanel4.add(serverTextField); jLabel2.setText("用户名:"); jPanel4.add(jLabel2); userTextField.setText("oyp"); userTextField.setMaximumSize(new java.awt.Dimension(200, 2147483647)); userTextField.setPreferredSize(new java.awt.Dimension(100, 21)); userTextField.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(java.awt.event.KeyEvent evt) { LinkFTPKeyPressed(evt); } }); jPanel4.add(userTextField); jLabel3.setText("密码:"); jPanel4.add(jLabel3); PassField.setText("oyp"); PassField.setMaximumSize(new java.awt.Dimension(200, 2147483647)); PassField.setPreferredSize(new java.awt.Dimension(100, 21)); PassField.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(java.awt.event.KeyEvent evt) { LinkFTPKeyPressed(evt); } }); jPanel4.add(PassField); jLabel6.setText("端口:"); jPanel4.add(jLabel6); portTextField.setText("21"); portTextField.setMaximumSize(new java.awt.Dimension(100, 2147483647)); portTextField.setPreferredSize(new java.awt.Dimension(50, 21)); portTextField.addKeyListener(new java.awt.event.KeyAdapter() { public void keyPressed(java.awt.event.KeyEvent evt) { LinkFTPKeyPressed(evt); } }); jPanel4.add(portTextField); linkButton.setText("连接"); linkButton.setFocusable(false); linkButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); linkButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); linkButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { linkButtonActionPerformed(evt); } }); jPanel4.add(linkButton); jPanel1.add(jPanel4); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; //指定包含组件的显示区域开始边的单元格,其中行的第一个单元格为 gridx=0。 gridBagConstraints.gridy = 0; //指定位于组件显示区域的顶部的单元格,其中最上边的单元格为 gridy=0。 //当组件的显示区域大于它所请求的显示区域的大小时使用此字段。 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; //在水平方向而不是垂直方向上调整组件大小。 gridBagConstraints.weightx = 1.0; //指定如何分布额外的水平空间。 getContentPane().add(jPanel1, gridBagConstraints); jSplitPane2.setBorder(null); jSplitPane2.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); jSplitPane2.setResizeWeight(1.0); jSplitPane2.setContinuousLayout(true); jSplitPane1.setDividerLocation(400); jSplitPane1.setDividerSize(10); jSplitPane1.setOneTouchExpandable(true); jSplitPane1.setRightComponent(ftpPanel); jSplitPane1.setLeftComponent(localPanel); jSplitPane2.setLeftComponent(jSplitPane1); jTabbedPane1.setMinimumSize(new java.awt.Dimension(40, 170)); jTabbedPane1.addTab("队列", queuePanel);// 添加队列面板 jTabbedPane1.addTab("上传队列", uploadPanel);// 添加上传面板 jTabbedPane1.addTab("下载队列", downloadPanel);// 添加下载面板 jSplitPane2.setBottomComponent(jTabbedPane1); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; //在水平方向和垂直方向上同时调整组件大小。 gridBagConstraints.weightx = 1.0; //指定如何分布额外的水平空间。 gridBagConstraints.weighty = 1.0; //指定如何分布额外的垂直空间。 getContentPane().add(jSplitPane2, gridBagConstraints); fileMenu.setMnemonic('f'); fileMenu.setText("站点(F)"); ftpManageMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.CTRL_MASK)); ftpManageMenuItem.setText("FTP站点管理(S)"); ftpManageMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // System.out.println("action"); FtpSiteDialog dialog = new FtpSiteDialog(FTPClientFrame.this); dialog.setVisible(true); } }); fileMenu.add(ftpManageMenuItem); fileMenu.add(jSeparator1); linkToMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_MASK)); linkToMenuItem.setText("连接到...(C)"); linkToMenuItem.setAction(LINK_TO_ACTION); fileMenu.add(linkToMenuItem); cutMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( java.awt.event.KeyEvent.VK_Z, java.awt.event.InputEvent.CTRL_MASK)); cutMenuItem.setText("断开(Z)"); cutMenuItem.setAction(CUT_LINK_ACTION); fileMenu.add(cutMenuItem); fileMenu.add(jSeparator2); exitMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke( java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.CTRL_MASK)); exitMenuItem.setText("退出(X)"); exitMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); fileMenu.add(exitMenuItem); MenuBar.add(fileMenu); JMenu localMenu = new JMenu(); localMenu.setMnemonic('l'); localMenu.setText("本地(L)"); uploadMenuItem.setMnemonic('U'); uploadMenuItem.setText("上传(U)"); uploadMenuItem.setAction(localPanel.getActionMap().get("uploadAction")); localMenu.add(uploadMenuItem); localMenu.add(jSeparator3); createFolderMenuItem.setMnemonic('C'); createFolderMenuItem.setText("新建文件夹(C)"); createFolderMenuItem.setAction(localPanel.getActionMap().get( "createFolderAction")); localMenu.add(createFolderMenuItem); renameMenuItem.setMnemonic('R'); renameMenuItem.setText("重命名(R)"); renameMenuItem.setAction(localPanel.getActionMap().get("renameAction")); localMenu.add(renameMenuItem); delMenuItem.setMnemonic('D'); delMenuItem.setText("删除(D)"); delMenuItem.setAction(localPanel.getActionMap().get("delAction")); localMenu.add(delMenuItem); JMenuItem localrefreshMenuItem = new JMenuItem(); localrefreshMenuItem.setMnemonic('R'); localrefreshMenuItem.setText("刷新(R)"); localrefreshMenuItem.setAction(localPanel.getActionMap().get( "refreshAction")); localMenu.add(localrefreshMenuItem); MenuBar.add(localMenu); ftpMenu.setMnemonic('r'); ftpMenu.setText("远程(R)"); downMenuItem.setMnemonic('U'); downMenuItem.setText("下载(U)"); downMenuItem.setAction(ftpPanel.getActionMap().get("downAction")); ftpMenu.add(downMenuItem); ftpMenu.add(jSeparator6); ftpDelMenuItem.setMnemonic('D'); ftpDelMenuItem.setText("删除(D)"); ftpDelMenuItem.setAction(ftpPanel.getActionMap().get("delAction")); ftpMenu.add(ftpDelMenuItem); ftpRenameMenuItem.setMnemonic('R'); ftpRenameMenuItem.setText("重命名(R)"); ftpRenameMenuItem .setAction(ftpPanel.getActionMap().get("renameAction")); ftpMenu.add(ftpRenameMenuItem); newFolderMenuItem.setMnemonic('C'); newFolderMenuItem.setText("新建文件夹(C)"); newFolderMenuItem.setAction(ftpPanel.getActionMap().get( "createFolderAction")); ftpMenu.add(newFolderMenuItem); JMenuItem refreshMenuItem = new JMenuItem(); refreshMenuItem.setMnemonic('R'); refreshMenuItem.setText("刷新(R)"); refreshMenuItem.setAction(ftpPanel.getActionMap().get("refreshAction")); ftpMenu.add(refreshMenuItem); MenuBar.add(ftpMenu); helpMenu.setText("帮助(H)"); aboutMenuItem.setMnemonic('a'); aboutMenuItem.setText("关于(A)"); aboutMenuItem.addActionListener(new AboutItemAction(this)); helpMenu.add(aboutMenuItem); bugMenuItem.setMnemonic('u'); bugMenuItem.setText("错误报告(U)"); bugMenuItem.addActionListener(new BugItemAction()); helpMenu.add(bugMenuItem); MenuBar.add(helpMenu); setJMenuBar(MenuBar); java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit() .getScreenSize(); setBounds((screenSize.width - 800) / 2, (screenSize.height - 600) / 2, 800, 700); } public JToggleButton getShutdownButton() { return shutdownButton; } /** * 窗体装载的事件处理方法 */ private void formWindowOpened(java.awt.event.WindowEvent evt) { jSplitPane1.setDividerLocation(0.50); localPanel.getLocalDiskComboBox().setSelectedIndex(1); localPanel.getLocalDiskComboBox().setSelectedIndex(0); } /** * 窗体大小调整的事件处理方法 */ private void formComponentResized(java.awt.event.ComponentEvent evt) { jSplitPane1.setDividerLocation(0.50); } /** * 连接按钮的事件处理方法 */ private void linkButtonActionPerformed(java.awt.event.ActionEvent evt) { try { String server = serverTextField.getText(); // 获取服务器地址 if (server == null) { return; } String portStr = portTextField.getText(); // 获取端口号 if (portStr == null) { portStr = "21"; } int port = Integer.parseInt(portStr.trim()); String userStr = userTextField.getText(); // 获取用户名 userStr = userStr == null ? "" : userStr.trim(); String passStr = PassField.getText(); // 获取密码 passStr = passStr == null ? "" : passStr.trim(); cutLinkButton.doClick(); ftpClient = new FtpClient(); ftpClient.openServer(server.trim(), port); // 连接服务器 ftpClient.login(userStr, passStr); // 登录服务器 ftpClient.binary(); // 使用二进制传输模式 if (ftpClient.serverIsOpen()) { // 如果连接成功 CUT_LINK_ACTION.setEnabled(true); // 设置断开按钮可用 } else { // 否则 CUT_LINK_ACTION.setEnabled(false); // 设置断开按钮不可用 return; // 并结束事件处理 } // 设置本地资源管理面板的FTP连接信息 localPanel.setFtpClient(server, port, userStr, passStr); // 设置上传按钮可用 localPanel.getActionMap().get("uploadAction").setEnabled(true); ftpPanel.setFtpClient(ftpClient);// 设置FTP资源管理面板的FTP连接信息 // 设置下载按钮可用 ftpPanel.getActionMap().get("downAction").setEnabled(true); ftpPanel.refreshCurrentFolder();// 刷新FTP资源管理面板的当前文件夹 queuePanel.startQueue(); // 启动任务队列线程 } catch (Exception ex) { ex.printStackTrace(); } } /** * 连接FTP相关的文本框 和密码框的回车事件 */ private void LinkFTPKeyPressed(java.awt.event.KeyEvent evt) { if (evt.getKeyChar() == '\n') { linkButton.doClick(); } } public LocalPanel getLocalPanel() { return localPanel; } public FtpPanel getFtpPanel() { return ftpPanel; } public QueuePanel getQueuePanel() { return queuePanel; } public UploadPanel getUploadPanel() { return uploadPanel; } public DownloadPanel getDownloadPanel() { return downloadPanel; } public FtpClient getFtpClient() { return ftpClient; } /** * 设置FTP连接信息的方法,由FTP站点管理器调用 */ public void setLinkInfo(SiteInfoBean bean) { serverTextField.setText(bean.getServer()); // 设置主机地址 portTextField.setText(bean.getPort() + ""); // 设置端口号 userTextField.setText(bean.getUserName()); // 设置用户名 PassField.setText(""); // 密码清空 PassField.requestFocus(); // 密码框请求焦点 } }
整体界面如下:
五、FTP软件本地窗口的实现
1、首先看一下本地窗口的布局效果
2、看一下本地窗口实现的代码框架
2、本地窗口的具体实现代码LocalPanel.java
package com.oyp.ftp.panel.local; import java.awt.Color; import java.awt.Desktop; import java.awt.Dimension; import java.awt.event.ItemEvent; import java.io.File; import java.io.IOException; import java.util.Date; import java.util.LinkedList; import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.ActionMap; import javax.swing.DefaultComboBoxModel; import javax.swing.JButton; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import javax.swing.table.TableStringConverter; import com.oyp.ftp.FTPClientFrame; import com.oyp.ftp.panel.FTPTableCellRanderer; import com.oyp.ftp.panel.ftp.TableConverter; import com.oyp.ftp.utils.DiskFile; public class LocalPanel extends javax.swing.JPanel { Queue<Object[]> queue = new LinkedList<Object[]>(); private UploadThread uploadThread = null; private Desktop desktop = null; private javax.swing.JButton createFolderButton; private javax.swing.JButton delButton; private javax.swing.JScrollPane scrollPane; private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JToolBar toolBar; private javax.swing.JComboBox localDiskComboBox; javax.swing.JTable localDiskTable; javax.swing.JLabel localSelFilePathLabel; private javax.swing.JButton renameButton; private javax.swing.JButton uploadButton; private TableRowSorter<TableModel> sorter; FTPClientFrame frame = null; public LocalPanel() { initComponents(); } public LocalPanel(FTPClientFrame client_Frame) { frame = client_Frame; if (Desktop.isDesktopSupported()) { desktop = Desktop.getDesktop(); } initComponents(); } /** * 界面布局与初始化方法 */ private void initComponents() { ActionMap actionMap = getActionMap(); actionMap.put("delAction", new DelFileAction(this, "删除", null)); actionMap.put("renameAction", new RennameAction(this, "重命名", null)); actionMap.put("createFolderAction", new CreateFolderAction(this, "新建文件夹", null)); actionMap.put("uploadAction", new UploadAction(this, "上传", null)); actionMap.put("refreshAction", new RefreshAction(this, "刷新", null)); java.awt.GridBagConstraints gridBagConstraints; toolBar = new javax.swing.JToolBar(); delButton = new javax.swing.JButton(); renameButton = new javax.swing.JButton(); createFolderButton = new javax.swing.JButton(); uploadButton = new javax.swing.JButton(); jSeparator1 = new javax.swing.JToolBar.Separator(); localDiskComboBox = new javax.swing.JComboBox(); localDiskComboBox.setPreferredSize(new Dimension(100, 25)); scrollPane = new javax.swing.JScrollPane(); localDiskTable = new javax.swing.JTable(); localDiskTable.setDragEnabled(true); localSelFilePathLabel = new javax.swing.JLabel(); /** * 向现有边框添加一个标题,使其具有指定的位置和默认字体和文本颜色(由当前外观确定)。 * TitledBorder.CENTER: 将标题文本置于边框线的中心。 * TitledBorder.ABOVE_TOP: 将标题置于边框顶端线的上部。 */ setBorder(javax.swing.BorderFactory.createTitledBorder(null, "本地", javax.swing.border.TitledBorder.CENTER, javax.swing.border.TitledBorder.ABOVE_TOP)); setLayout(new java.awt.GridBagLayout()); toolBar.setRollover(true); toolBar.setFloatable(false); delButton.setText("删除"); delButton.setFocusable(false); delButton.setAction(actionMap.get("delAction")); toolBar.add(delButton); renameButton.setText("重命名"); renameButton.setFocusable(false); renameButton.setAction(actionMap.get("renameAction")); toolBar.add(renameButton); createFolderButton.setText("新文件夹"); createFolderButton.setFocusable(false); createFolderButton.setAction(actionMap.get("createFolderAction")); toolBar.add(createFolderButton); uploadButton.setText("上传"); uploadButton.setFocusable(false); uploadButton.setAction(actionMap.get("uploadAction")); toolBar.add(uploadButton); JButton refreshButton = new JButton(); refreshButton.setText("刷新"); refreshButton.setFocusable(false); refreshButton.setAction(actionMap.get("refreshAction")); toolBar.add(refreshButton); toolBar.add(jSeparator1); //File.listRoots():列出可用的文件系统根。 localDiskComboBox.setModel(new DefaultComboBoxModel(File.listRoots())); localDiskComboBox.addItemListener(new java.awt.event.ItemListener() { public void itemStateChanged(java.awt.event.ItemEvent evt) { localDiskComboBoxItemStateChanged(evt); } }); toolBar.add(localDiskComboBox); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 1; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; add(toolBar, gridBagConstraints); localDiskTable.setModel(new LocalTableModel()); localDiskTable.setShowHorizontalLines(false); localDiskTable.setShowVerticalLines(false); localDiskTable.getTableHeader().setReorderingAllowed(false); localDiskTable.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { localDiskTableMouseClicked(evt); } }); scrollPane.setViewportView(localDiskTable); scrollPane.getViewport().setBackground(Color.WHITE); //设置渲染本地资源和FTP资源表格组件的渲染器 localDiskTable.getColumnModel().getColumn(0).setCellRenderer( FTPTableCellRanderer.getCellRanderer()); //RowSorter 的一个实现,它使用 TableModel 提供排序和过滤操作。 sorter = new TableRowSorter<TableModel>(localDiskTable.getModel()); TableStringConverter converter = new TableConverter(); //设置负责将值从模型转换为字符串的对象。 sorter.setStringConverter(converter); //设置 RowSorter。RowSorter 用于提供对 JTable 的排序和过滤。 localDiskTable.setRowSorter(sorter); sorter.toggleSortOrder(0); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; add(scrollPane, gridBagConstraints); localSelFilePathLabel.setBorder(javax.swing.BorderFactory .createEtchedBorder()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; add(localSelFilePathLabel, gridBagConstraints); } /** * 本地磁盘下拉选择框的选项改变事件处理方法,由事件监听器调用 */ private void localDiskComboBoxItemStateChanged(java.awt.event.ItemEvent evt) { if (evt.getStateChange() == ItemEvent.SELECTED) { Object item = evt.getItem(); // 获取选择的下拉列表的选项 if (item instanceof File) { // 如果该选项是File类的实例对象 File selDisk = (File) item; // 将该选项转换成File类 // 调用listLocalFiles()方法,显示该File类指定的磁盘文件列表 listLocalFiles(selDisk); } } } /** * 刷新指定文件夹的方法 */ void refreshFolder(File file) { listLocalFiles(file); } /** * 刷新本地当前文件夹的方法 */ public void refreshCurrentFolder() { final File file = getCurrentFolder(); // 获取当前文件夹 Runnable runnable = new Runnable() { // 创建新的线程 public void run() { listLocalFiles(file); // 重载当前文件夹的列表到表格中 } }; //导致 runnable 的 run 方法在 EventQueue 的指派线程上被调用。 SwingUtilities.invokeLater(runnable); // 在事件线程中调用该线程对象 } /** * 获取当前文件夹 */ public File getCurrentFolder() { // 使用路径标签的路径创建当前文件夹对象 File file = new File(localSelFilePathLabel.getText()); // 如果表格选择了文件夹,或选择的文件有真是的上级文件夹 if (localDiskTable.getSelectedRow() > 1 && file.getParentFile() != null) file = file.getParentFile(); // 获取该上级文件夹 return file; // 返回文件夹对象 } /** * 本地磁盘文件的表格单击和双击事件处理方法 */ private void localDiskTableMouseClicked(java.awt.event.MouseEvent evt) { int selectedRow = localDiskTable.getSelectedRow(); // 获取选择的表格行号 if (selectedRow < 0) return; // 获取表格中选择的当前行的第一个字段的值 Object value = localDiskTable.getValueAt(selectedRow, 0); if (value instanceof DiskFile) { // 如果该值是DiskFile的实例对象 DiskFile selFile = (DiskFile) value; // 设置状态栏的本地文件路径 localSelFilePathLabel.setText(selFile.getAbsolutePath()); if (evt.getClickCount() >= 2) { // 如果是双击鼠标 if (selFile.isDirectory()) { // 并且选择的是文件夹 listLocalFiles(selFile); // 显示该文件夹的内容列表 } else if (desktop != null) { // 如果不是文件夹 try { desktop.open(selFile); // 关联本地系统程序打开该文件 } catch (IOException ex) { Logger.getLogger(FTPClientFrame.class.getName()).log( Level.SEVERE, null, ex); } } } } else { // 如果选择的表格内容不是DiskFile类的实例 // 判断选择的是不是..选项 if (evt.getClickCount() >= 2 && value.equals("..")) { // 创建当前选择文件的临时文件 File tempFile = new File((localSelFilePathLabel.getText())); // 显示选择的文件的上级目录列表 listLocalFiles(tempFile.getParentFile()); } } } /** * 读取本地文件到表格的方法 */ private void listLocalFiles(File selDisk) { if (selDisk == null || selDisk.isFile()) { return; } localSelFilePathLabel.setText(selDisk.getAbsolutePath()); File[] listFiles = selDisk.listFiles(); // 获取磁盘文件列表 // 获取表格的数据模型 DefaultTableModel model = (DefaultTableModel) localDiskTable.getModel(); model.setRowCount(0); // 清除模型的内容 model.addRow(new Object[] { ".", "<DIR>", "" }); // 创建.选项 model.addRow(new Object[] { "..", "<DIR>", "" }); // 创建..选项 if (listFiles == null) { JOptionPane.showMessageDialog(this, "该磁盘无法访问"); return; } // 遍历磁盘根文件夹的内容,添加到表格中 for (File file : listFiles) { File diskFile = new DiskFile(file); // 创建文件对象 String length = file.length() + "B "; // 获取文件大小 if (file.length() > 1000 * 1000 * 1000) { // 计算文件G单位 length = file.length() / 1000000000 + "G "; } if (file.length() > 1000 * 1000) { // 计算文件M单位 length = file.length() / 1000000 + "M "; } if (file.length() > 1000) { length = file.length() / 1000 + "K "; // 计算文件K单位 } if (file.isDirectory()) { // 显示文件夹标志 length = "<DIR>"; } // 获取文件的最后修改日期 String modifDate = new Date(file.lastModified()).toLocaleString(); if (!file.canRead()) { length = "未知"; modifDate = "未知"; } // 将单个文件的信息添加到表格的数据模型中 model.addRow(new Object[] { diskFile, length, modifDate }); } localDiskTable.clearSelection(); // 取消表格的选择项 } /** * 停止文件上传线程的方法 */ public void stopUploadThread() { if (uploadThread != null) uploadThread.stopThread(); } public javax.swing.JComboBox getLocalDiskComboBox() { return localDiskComboBox; } /** * 设置FTP连接,并启动上传队列线程的方法。 */ public void setFtpClient(String server, int port, String userStr, String passStr) { if (uploadThread != null) uploadThread.stopThread(); uploadThread = new UploadThread(this, server, port, userStr, passStr); uploadThread.start(); } public Queue<Object[]> getQueue() { return queue; } }六、FTP软件远程窗口的实现
1、首先看一下远程窗口的布局效果
2、看一下本地窗口实现的代码框架
3、远程窗口主要实现代码FtpPanel.java
package com.oyp.ftp.panel.ftp; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import java.util.LinkedList; import java.util.Queue; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.ActionMap; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableModel; import javax.swing.table.TableRowSorter; import javax.swing.table.TableStringConverter; import sun.net.TelnetInputStream; import com.oyp.ftp.FTPClientFrame; import com.oyp.ftp.panel.FTPTableCellRanderer; import com.oyp.ftp.utils.FtpClient; import com.oyp.ftp.utils.FtpFile; public class FtpPanel extends javax.swing.JPanel { FtpClient ftpClient; private javax.swing.JButton createFolderButton; private javax.swing.JButton delButton; private javax.swing.JButton downButton; javax.swing.JTable ftpDiskTable; private javax.swing.JLabel ftpSelFilePathLabel; private javax.swing.JScrollPane scrollPane; private javax.swing.JToolBar toolBar; private javax.swing.JButton refreshButton; private javax.swing.JButton renameButton; FTPClientFrame frame = null; Queue<Object[]> queue = new LinkedList<Object[]>(); private DownThread thread; public FtpPanel() { initComponents(); } public FtpPanel(FTPClientFrame client_Frame) { frame = client_Frame; initComponents(); } private void initComponents() { ActionMap actionMap = getActionMap(); actionMap.put("createFolderAction", new CreateFolderAction(this, "创建文件夹", null)); actionMap.put("delAction", new DelFileAction(this, "删除", null)); actionMap.put("refreshAction", new RefreshAction(this, "刷新", null)); actionMap.put("renameAction", new RenameAction(this, "重命名", null)); actionMap.put("downAction", new DownAction(this, "下载", null)); java.awt.GridBagConstraints gridBagConstraints; toolBar = new javax.swing.JToolBar(); delButton = new javax.swing.JButton(); renameButton = new javax.swing.JButton(); createFolderButton = new javax.swing.JButton(); downButton = new javax.swing.JButton(); refreshButton = new javax.swing.JButton(); scrollPane = new JScrollPane(); ftpDiskTable = new JTable(); ftpDiskTable.setDragEnabled(true); ftpSelFilePathLabel = new javax.swing.JLabel(); setBorder(javax.swing.BorderFactory.createTitledBorder(null, "远程", javax.swing.border.TitledBorder.CENTER, javax.swing.border.TitledBorder.ABOVE_TOP)); setLayout(new java.awt.GridBagLayout()); toolBar.setRollover(true); toolBar.setFloatable(false); delButton.setText("删除"); delButton.setFocusable(false); delButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); delButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); delButton.setAction(actionMap.get("delAction")); toolBar.add(delButton); renameButton.setText("重命名"); renameButton.setFocusable(false); renameButton.setAction(actionMap.get("renameAction")); toolBar.add(renameButton); createFolderButton.setText("新文件夹"); createFolderButton.setFocusable(false); createFolderButton.setAction(actionMap.get("createFolderAction")); toolBar.add(createFolderButton); downButton.setText("下载"); downButton.setFocusable(false); downButton.setAction(actionMap.get("downAction")); toolBar.add(downButton); refreshButton.setText("刷新"); refreshButton.setFocusable(false); refreshButton.setAction(actionMap.get("refreshAction")); toolBar.add(refreshButton); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; gridBagConstraints.weightx = 1.0; add(toolBar, gridBagConstraints); ftpDiskTable.setModel(new FtpTableModel()); ftpDiskTable.setShowHorizontalLines(false); ftpDiskTable.setShowVerticalLines(false); ftpDiskTable.getTableHeader().setReorderingAllowed(false); ftpDiskTable.setDoubleBuffered(true); ftpDiskTable.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { ftpDiskTableMouseClicked(evt); } }); scrollPane.setViewportView(ftpDiskTable); scrollPane.getViewport().setBackground(Color.WHITE); //设置渲染本地资源和FTP资源表格组件的渲染器 ftpDiskTable.getColumnModel().getColumn(0).setCellRenderer( FTPTableCellRanderer.getCellRanderer()); //RowSorter 的一个实现,它使用 TableModel 提供排序和过滤操作。 TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>( ftpDiskTable.getModel()); TableStringConverter converter = new TableConverter(); //设置负责将值从模型转换为字符串的对象。 sorter.setStringConverter(converter); //设置 RowSorter。RowSorter 用于提供对 JTable 的排序和过滤。 ftpDiskTable.setRowSorter(sorter); /** * 颠倒指定列的排序顺序。调用此方法时,由子类提供具体行为。 * 通常,如果指定列已经是主要排序列,则此方法将升序变为降序(或将降序变为升序); * 否则,使指定列成为主要排序列,并使用升序排序顺序。如果指定列不可排序,则此方法没有任何效果。 */ sorter.toggleSortOrder(0); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 2; gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; gridBagConstraints.weightx = 1.0; gridBagConstraints.weighty = 1.0; add(scrollPane, gridBagConstraints); ftpSelFilePathLabel.setBorder(javax.swing.BorderFactory .createEtchedBorder()); gridBagConstraints = new java.awt.GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 3; gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; add(ftpSelFilePathLabel, gridBagConstraints); } /** * 表格单击或双击事件的处理方法。 */ private void ftpDiskTableMouseClicked(java.awt.event.MouseEvent evt) { int selectedRow = ftpDiskTable.getSelectedRow(); Object value = ftpDiskTable.getValueAt(selectedRow, 0); if (value instanceof FtpFile) { FtpFile selFile = (FtpFile) value; ftpSelFilePathLabel.setText(selFile.getAbsolutePath()); if (evt.getClickCount() >= 2) { //双击鼠标 if (selFile.isDirectory()) { try { ftpClient.cd(selFile.getAbsolutePath()); listFtpFiles(ftpClient.list()); } catch (IOException ex) { ex.printStackTrace(); } } } } } /** * 读取FTP文件到表格的方法 * @param list * 读取FTP服务器资源列表的输入流 */ public synchronized void listFtpFiles(final TelnetInputStream list) { // 获取表格的数据模型 final DefaultTableModel model = (DefaultTableModel) ftpDiskTable .getModel(); model.setRowCount(0); // 创建一个线程类 Runnable runnable = new Runnable() { public synchronized void run() { ftpDiskTable.clearSelection(); try { String pwd = getPwd(); // 获取FTP服务器的当前文件夹 model.addRow(new Object[] { new FtpFile(".", pwd, true), "", "" }); // 添加“.”符号 model.addRow(new Object[] { new FtpFile("..", pwd, true), "", "" }); // 添加“..”符号 /* byte[]names=new byte[2048]; int bufsize=0; bufsize=list.read(names, 0, names.length); // list.close(); int i=0,j=0; while(i<bufsize){ char bc=(char)names[i]; System.out.print(i+" "+bc+" "); //文件名在数据中开始做坐标为j,i-j为文件名的长度,文件名在数据中的结束下标为i-1 if (names[i]==13) { // System.out.println("j:"+j+" i:"+i+ " i-j:"+(i-j)); String temName=new String(names,j,i-j); System.out.println("temName="+temName); j=i+2; } i=i+1; } */ /* 其中格式应满足如下格式的字符串 结果为: 0 -: 1 r: 2 w: 3 x: 4 -: 5 -: 6 -: 7 -: 8 -: 9 -: 10 : 11 1: 12 : 13 u: 14 s: 15 e: 16 r: 17 : 18 g: 19 r: 20 o: 21 u: 22 p: 23 : 24 : 25 : 26 : 27 : 28 : 29 : 30 : 31 : 32 6: 33 7: 34 8: 35 4: 36 3: 37 0: 38 : 39 A: 40 p: 41 r: 42 : 43 1: 44 6: 45 : 46 2: 47 1: 48 :: 49 4: 50 6: 51 : 52 F: 53 T: 54 P: 55 ?: 56 ?: 57 ?: 58 ?: 59 ?: 60 ?: 61 ?: 62 ?: 63 ?: 64 ?: 65 ?: 66 ?: 67 ?: 68 ?: 69 ?: 70 ?: 71 ?: 72 ?: 73 .: 74 p: 75 d: 76 f: 77 -rwx------ 1 user group 678430 Apr 16 21:46 FTP客户端的设计与实现.pdf -rwx------ 1 user group 87504927 Apr 18 22:50 VC.深入详解(孙鑫)[www.xuexi111.com].pdf -rwx------ 1 user group 57344 Apr 18 05:32 腾讯电商2013实习生招聘TST推荐模板.xls *<br>d 表示目录 * <br>- 表示文件 * <br>rw-rw-rw- 表示权限设置 dateStr:39-51 sizeOrDir:23-38 fileName:52-^ */ /*********************************************************/ byte[]names=new byte[2048]; int bufsize=0; bufsize=list.read(names, 0, names.length); int i=0,j=0; while(i<bufsize){ //字符模式为10,二进制模式为13 // if (names[i]==10) { if (names[i]==13) { //获取字符串 -rwx------ 1 user group 57344 Apr 18 05:32 腾讯电商2013实习生招聘TST推荐模板.xls //文件名在数据中开始做坐标为j,i-j为文件名的长度,文件名在数据中的结束下标为i-1 String fileMessage = new String(names,j,i-j); if(fileMessage.length() == 0){ System.out.println("fileMessage.length() == 0"); break; } //按照空格将fileMessage截为数组后获取相关信息 // 正则表达式 \s表示空格,{1,}表示1一个以上 if(!fileMessage.split("\\s+")[8].equals(".") && !fileMessage.split("\\s+")[8].equals("..")){ /**文件大小*/ String sizeOrDir=""; if (fileMessage.startsWith("d")) {//如果是目录 sizeOrDir="<DIR>"; }else if (fileMessage.startsWith("-")) {//如果是文件 sizeOrDir=fileMessage.split("\\s+")[4]; } /**文件名*/ String fileName=fileMessage.split("\\s+")[8]; /**文件日期*/ String dateStr =fileMessage.split("\\s+")[5] +" "+fileMessage.split("\\s+")[6]+" " +fileMessage.split("\\s+")[7]; // System.out.println("sizeOrDir="+sizeOrDir); // System.out.println("fileName="+fileName); // System.out.println("dateStr="+dateStr); FtpFile ftpFile = new FtpFile(); // 将FTP目录信息初始化到FTP文件对象中 ftpFile.setLastDate(dateStr); ftpFile.setSize(sizeOrDir); ftpFile.setName(fileName); ftpFile.setPath(pwd); // 将文件信息添加到表格中 model.addRow(new Object[] { ftpFile, ftpFile.getSize(), dateStr }); } // j=i+1;//上一次位置为字符模式 j=i+2;//上一次位置为二进制模式 } i=i+1; } list.close(); /********************************************************************** //下面的方法太死了,不够灵活 BufferedReader br = new BufferedReader( new InputStreamReader(list)); // 创建字符输入流 String data = null; // 读取输入流中的文件目录 while ((data = br.readLine()) != null) { // 创建FTP文件对象 FtpFile ftpFile = new FtpFile(); // 获取FTP服务器目录信息 String dateStr = data.substring(39, 51).trim(); String sizeOrDir = data.substring(23, 38).trim(); String fileName = data.substring(52, data.length()) .trim(); // 将FTP目录信息初始化到FTP文件对象中 ftpFile.setLastDate(dateStr); ftpFile.setSize(sizeOrDir); ftpFile.setName(fileName); ftpFile.setPath(pwd); // 将文件信息添加到表格中 model.addRow(new Object[] { ftpFile, ftpFile.getSize(), dateStr }); } br.close(); // 关闭输入流 **********************************************************************/ } catch (IOException ex) { Logger.getLogger(FTPClientFrame.class.getName()).log( Level.SEVERE, null, ex); } } }; if (SwingUtilities.isEventDispatchThread()) // 启动线程对象 runnable.run(); else SwingUtilities.invokeLater(runnable); } /** * 设置FTP连接,并启动下载队列线程的方法 */ public void setFtpClient(FtpClient ftpClient) { this.ftpClient = ftpClient; // 以30秒为间隔与服务器保持通讯 final Timer timer = new Timer(3000, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { try { final FtpClient ftpClient = FtpPanel.this.ftpClient; if (ftpClient != null && ftpClient.serverIsOpen()) { ftpClient.noop(); } } catch (IOException e1) { e1.printStackTrace(); } } }); timer.start(); startDownThread(); } /** * 刷新FTP资源管理面板的当前文件夹 */ public void refreshCurrentFolder() { try { // 获取服务器文件列表 TelnetInputStream list = ftpClient.list(); listFtpFiles(list); // 调用解析方法 } catch (IOException e) { e.printStackTrace(); } } /** * 开始下载队列线程 */ private void startDownThread() { if (thread != null) thread.stopThread(); thread = new DownThread(this); thread.start(); } /** * 停止下载队列线程 */ public void stopDownThread() { if (thread != null) { thread.stopThread(); ftpClient = null; } } public String getPwd() { String pwd = null; try { pwd = ftpClient.pwd(); } catch (IOException e) { e.printStackTrace(); } return pwd; } public Queue<Object[]> getQueue() { return queue; } /** * 清除FTP资源表格内容的方法 */ public void clearTable() { FtpTableModel model = (FtpTableModel) ftpDiskTable.getModel(); model.setRowCount(0); } }七、上传下载队列窗口的实现
1、首先看一下队列窗口的界面
2、看一下上传队列窗口的界面
3、看一下下载队列窗口的界面
4.队列窗口的实现
package com.oyp.ftp.panel.queue; import static java.awt.BorderLayout.CENTER; import static java.awt.BorderLayout.EAST; import static javax.swing.ListSelectionModel.SINGLE_SELECTION; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.util.LinkedList; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.ListSelectionModel; import javax.swing.Timer; import javax.swing.table.DefaultTableModel; import com.oyp.ftp.FTPClientFrame; import com.oyp.ftp.utils.FtpClient; import com.oyp.ftp.utils.FtpFile; /** * 任务队列控制面板 */ public class QueuePanel extends JPanel implements ActionListener { private JTable queueTable = new JTable(); // 显示任务队列的表格组件 private JScrollPane scrollPane = new JScrollPane(); private FTPClientFrame frame; // 主窗体的引用对象 private String[] columns; private FtpClient ftpClient; // FTP协议的控制类 private Timer queueTimer; // 队列的定时器 private LinkedList<Object[]> localQueue; // 本地面板的上传队列 private LinkedList<Object[]> ftpQueue; // FTP面板的下载队列 private JToggleButton stopButton; private boolean stop = false; // 队列的控制变量 /** * 默认的构造方法 */ public QueuePanel() { initComponent(); } /** * 自定义的构造方法 * * @param frame * 主窗体 */ public QueuePanel(FTPClientFrame frame) { this.frame = frame; // 从主窗体获取本地面板的上传队列 localQueue = (LinkedList<Object[]>) frame.getLocalPanel().getQueue(); // 从主窗体获取FTP面板的下载队列 ftpQueue = (LinkedList<Object[]>) frame.getFtpPanel().getQueue(); initComponent(); // 初始化窗体界面 // 创建定时器,每间隔1秒执行队列刷新任务 queueTimer = new Timer(1000, new ActionListener() { /** * 定时器的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { if (localQueue.size() + ftpQueue.size() == queueTable .getRowCount()) // 如果队列大小没有改变 return; // 结束本方法,不做任何操作 refreshQueue(); // 否则刷新显示队列信息的表格数据 } }); } private void initComponent() { BorderLayout cardLayout = new BorderLayout(); setLayout(cardLayout); columns = new String[] { "任务名称", "方向", "主机", "执行状态" }; queueTable.setModel(new DefaultTableModel(new Object[][] {}, columns)); queueTable.getTableHeader().setReorderingAllowed(false); scrollPane.setViewportView(queueTable); cardLayout.layoutContainer(scrollPane); add(scrollPane, CENTER); JToolBar controlTool = new JToolBar(JToolBar.VERTICAL); controlTool.setRollover(true); controlTool.setFloatable(false); JButton upButton = new JButton("上移"); upButton.setActionCommand("up"); upButton.addActionListener(this); JButton downButton = new JButton("下移"); downButton.setActionCommand("down"); downButton.addActionListener(this); stopButton = new JToggleButton("暂停"); stopButton.setActionCommand("stop&start"); stopButton.addActionListener(this); JButton delButton = new JButton("删除"); delButton.setActionCommand("del"); delButton.addActionListener(this); JButton clearButton = new JButton("清空"); clearButton.setActionCommand("clear"); clearButton.addActionListener(this); controlTool.setLayout(new BoxLayout(controlTool, BoxLayout.Y_AXIS)); controlTool.add(upButton); controlTool.add(downButton); controlTool.add(stopButton); controlTool.add(delButton); controlTool.add(clearButton); add(controlTool, EAST); } public void startQueue() { ftpClient = frame.getFtpClient(); queueTimer.start(); } /** * 界面上按钮的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { final String command = e.getActionCommand(); if (command.equals("stop&start")) {// 处理暂停按钮事件 if (stopButton.isSelected()) { stop = true; stopButton.setText("继续"); } else { stop = false; stopButton.setText("暂停"); } } // 处理上移和下移按钮事件 if (command.equals("up") || command.equals("down")) { up_Down_Action(command); // 调用处理上移和下移动作的方法 } if (command.equals("del")) {// 处理删除按钮的事件 int row = queueTable.getSelectedRow(); // 获取显示队列的表格的当前选择行 if (row < 0) return; // 获取选择行的第一个表格单元值 Object valueAt = queueTable.getValueAt(row, 0); // 如果选择内容是File类的对象 if (valueAt instanceof File) { File file = (File) valueAt; int size = localQueue.size(); // 获取上传队列大小 for (int i = 0; i < size; i++) { // 遍历上传队列 if (localQueue.get(i)[0].equals(file)) { localQueue.remove(i); // 从上传队列中删除文件对象 } } } else if (valueAt instanceof String) { // 如果选择的是字符串对象 String fileStr = (String) valueAt; int size = ftpQueue.size(); // 获取上传队列的大小 for (int i = 0; i < size; i++) { // 遍历上传队列 // 获取上传队列中的文件对象 FtpFile ftpFile = (FtpFile) ftpQueue.get(i)[0]; if (ftpFile.getAbsolutePath().equals(fileStr)) { ftpQueue.remove(i); // 从上传队列中删除该文件对象 } } } refreshQueue(); // 刷新队列列表 } if (command.equals("clear")) { // 处理清空按钮的事件 localQueue.clear(); // 调用本地面板的队列的clear()方法 ftpQueue.clear(); // 调用FTP面板的队列的clear()方法 } } /** * 队列任务的上移和下移动作处理方法 * * @param command * 上移或下移命令 */ private void up_Down_Action(final String command) { int row = queueTable.getSelectedRow(); // 获取队列表格的当前选择行号 if (row < 0) return; // 获取当前选择行的第一个单元值 Object valueAt = queueTable.getValueAt(row, 0); // 获取当前选择行的第二个单元值作为判断上传或下载方向的依据 String orientation = (String) queueTable.getValueAt(row, 1); if (orientation.equals("上传")) { // 如果是上传任务 String value = ((File) valueAt).getAbsolutePath(); int size = localQueue.size(); for (int i = 0; i < size; i++) { // 遍历上传队列 Object[] que = localQueue.get(i); File file = (File) que[0]; // 从队列中,遍历到选择的上传任务的文件对象 if (file.getAbsolutePath().equals(value)) { ListSelectionModel selModel = queueTable .getSelectionModel(); // 获取表格的选择模型 selModel // 设置选择模型的单选模式 .setSelectionMode(ListSelectionModel.SINGLE_SELECTION); int dsize = localQueue.size(); // 获取本地上传队列的大小 int drow = queueTable.getSelectedRow();// 获取队列表格的当前选择行号 int queueVal = localQueue.size() - dsize; int next = -1; int selIndex = -1; if (command.equals("up")) { if (i < 1) // 限制选择范围 return; selIndex = drow - queueVal - 1; next = i - 1; } else { if (i >= size - 1) // 限制选择范围 return; selIndex = drow - queueVal + 1; next = i + 1; } // 交换连个队列位置的任务 Object[] temp = localQueue.get(next); localQueue.set(next, que); localQueue.set(i, temp); refreshQueue(); // 刷新队列表格的列表 // 设置表格选择第一行 selModel.setSelectionInterval(0, selIndex); break; } } } else if (orientation.equals("下载")) { // 如果是下载任务 String value = (String) valueAt; int size = ftpQueue.size(); // 获取FTP下载队列的大小 for (int i = 0; i < size; i++) { // 遍历下载队列 Object[] que = ftpQueue.get(i); FtpFile file = (FtpFile) que[0]; // 获取每个下载任务的FTP文件对象 if (file.getAbsolutePath().equals(value)) {// ListSelectionModel selModel = queueTable .getSelectionModel(); // 获取任务队列表格的选择模型 // 设置模型使用单选模式 selModel.setSelectionMode(SINGLE_SELECTION); int dsize = ftpQueue.size(); int drow = queueTable.getSelectedRow(); int queueVal = ftpQueue.size() - dsize; int next = -1; int selIndex = -1; if (command.equals("up")) { if (i < 1) // 限制选择范围 return; selIndex = drow - queueVal - 1; next = i - 1; } else { if (i >= size - 1) // 限制选择范围 return; selIndex = drow - queueVal + 1; next = i + 1; } // 交换连个队列位置的任务内容 Object[] temp = ftpQueue.get(next); ftpQueue.set(next, que); ftpQueue.set(i, temp); refreshQueue(); // 刷新任务队列的表格列表 // 选择表格的第一行 selModel.setSelectionInterval(0, selIndex); break; } } } } /** * 刷新队列的方法 */ private synchronized void refreshQueue() { // 如果自动关机按钮被按下并且上传和下载的队列都有任务 if (frame.getShutdownButton().isSelected() && localQueue.isEmpty() && ftpQueue.isEmpty()) { try { // 执行系统关机命令,延迟30秒钟 Runtime.getRuntime().exec("shutdown -s -t 30"); } catch (IOException e) { e.printStackTrace(); } } // 创建表格的数据模型对象 DefaultTableModel model = new DefaultTableModel(columns, 0); // 获取本地上传队列中的任务 Object[] localQueueArray = localQueue.toArray(); // 遍历本地上传任务 for (int i = 0; i < localQueueArray.length; i++) { Object[] queueValue = (Object[]) localQueueArray[i]; if (queueValue == null) continue; File localFile = (File) queueValue[0]; // 把上传队列的任务添加到表格组件的数据模型中 model.addRow(new Object[] { localFile.getAbsoluteFile(), "上传", ftpClient.getServer(), i == 0 ? "正在上传" : "等待上传" }); } // 获取下载队列的任务 Object[] ftpQueueArray = ftpQueue.toArray(); // 遍历下载队列 for (int i = 0; i < ftpQueueArray.length; i++) { Object[] queueValue = (Object[]) ftpQueueArray[i]; if (queueValue == null) continue; FtpFile ftpFile = (FtpFile) queueValue[0]; // 把下载队列的任务添加到表格组件的数据模型中 model.addRow(new Object[] { ftpFile.getAbsolutePath(), "下载", ftpClient.getServer(), i == 0 ? "正在下载" : "等待下载" }); } queueTable.setModel(model); // 设置表格使用本方法的表格数据模型 } public boolean isStop() { return stop; } }5.上传队列窗口的实现
package com.oyp.ftp.panel.queue; import java.awt.CardLayout; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import com.oyp.ftp.panel.QueueTableCellRanderer; import com.oyp.ftp.utils.ProgressArg; public class UploadPanel extends JPanel { private JTable uploadTable = new JTable(); // 表格组件 private JScrollPane scrollPane = new JScrollPane(); private DefaultTableModel model; // 表格的数据模型 /** * 构造方法,用于初始化程序界面 */ public UploadPanel() { CardLayout cardLayout = new CardLayout(); setLayout(cardLayout); ProgressArg progressArg = new ProgressArg(-1, -1, -1); model = new DefaultTableModel(new Object[][] { new Object[] { "", "", "", "", progressArg } }, new String[] { "文件名", "大小", "远程文件名", "主机", "状态" }); uploadTable.setModel(model); uploadTable.getTableHeader().setReorderingAllowed(false); uploadTable.setRowSelectionAllowed(false); TableColumn column = uploadTable.getColumn("状态"); column.setCellRenderer(new QueueTableCellRanderer()); scrollPane.setViewportView(uploadTable); cardLayout.layoutContainer(scrollPane); add(scrollPane, "queue"); } /** * 向上传队列的表格组件添加新任务的方法 * * @param values * - 添加到表格的一行数据的数组对象 */ public void addRow(final Object[] values) { Runnable runnable = new Runnable() { public void run() { model.insertRow(0, values); // 向表格的数据模型添加数据 } }; if (SwingUtilities.isEventDispatchThread()) runnable.run(); // 在事件队列执行 else SwingUtilities.invokeLater(runnable); // 或有事件队列调用 } }6.下载队列窗口的实现
package com.oyp.ftp.panel.queue; import java.awt.CardLayout; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.SwingUtilities; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableColumn; import com.oyp.ftp.panel.QueueTableCellRanderer; import com.oyp.ftp.utils.ProgressArg; public class DownloadPanel extends JPanel { private JTable downloadTable = new JTable(); private JScrollPane scrollPane = new JScrollPane(); private DefaultTableModel model; public DownloadPanel() { CardLayout cardLayout = new CardLayout(); setLayout(cardLayout); ProgressArg progressArg = new ProgressArg(-1, -1, -1); model = new DefaultTableModel(new Object[][] { new Object[] { "", "", "", "", progressArg } }, new String[] { "文件名", "大小", "本地文件名", "主机", "状态" }); downloadTable.setModel(model); downloadTable.getTableHeader().setReorderingAllowed(false); downloadTable.setRowSelectionAllowed(false); TableColumn column = downloadTable.getColumn("状态"); column.setCellRenderer(new QueueTableCellRanderer()); scrollPane.setViewportView(downloadTable); cardLayout.layoutContainer(scrollPane); add(scrollPane, "queue"); } public void addRow(final Object[] values) { Runnable runnable = new Runnable() { public void run() { model.insertRow(0, values); } }; if (SwingUtilities.isEventDispatchThread()) runnable.run(); else SwingUtilities.invokeLater(runnable); } }下面窗口具体的上传下载后的变化
1.上传任务的添加
2.上传任务的完成
3.下载任务的添加
4.下载任务的完成
八、连接管理模块的实现:主机与服务器之间的连接与关闭操作
(1)FTP连接
运行FTP客户端后,首先是连接FTP服务器,需要输入FTP服务器的IP地址及用户名、密码以及端口号后点击连接按钮开始连接FTP服务器,连接流程图如下图所示。
点击“连接”按钮后,会调用com.oyp.ftp.FTPClientFrame类的linkButtonActionPerformed(ActionEvent evt)方法,其主要代码程序如下
/** * 连接按钮的事件处理方法 */ private void linkButtonActionPerformed(java.awt.event.ActionEvent evt) { try { String server = serverTextField.getText(); // 获取服务器地址 if (server == null) { return; } String portStr = portTextField.getText(); // 获取端口号 if (portStr == null) { portStr = "21"; } int port = Integer.parseInt(portStr.trim()); String userStr = userTextField.getText(); // 获取用户名 userStr = userStr == null ? "" : userStr.trim(); String passStr = PassField.getText(); // 获取密码 passStr = passStr == null ? "" : passStr.trim(); cutLinkButton.doClick(); ftpClient = new FtpClient(); ftpClient.openServer(server.trim(), port); // 连接服务器 ftpClient.login(userStr, passStr); // 登录服务器 ftpClient.binary(); // 使用二进制传输模式 if (ftpClient.serverIsOpen()) { // 如果连接成功 CUT_LINK_ACTION.setEnabled(true); // 设置断开按钮可用 } else { // 否则 CUT_LINK_ACTION.setEnabled(false); // 设置断开按钮不可用 return; // 并结束事件处理 } // 设置本地资源管理面板的FTP连接信息 localPanel.setFtpClient(server, port, userStr, passStr); // 设置上传按钮可用 localPanel.getActionMap().get("uploadAction").setEnabled(true); ftpPanel.setFtpClient(ftpClient);// 设置FTP资源管理面板的FTP连接信息 // 设置下载按钮可用 ftpPanel.getActionMap().get("downAction").setEnabled(true); ftpPanel.refreshCurrentFolder();// 刷新FTP资源管理面板的当前文件夹 queuePanel.startQueue(); // 启动任务队列线程 } catch (Exception ex) { ex.printStackTrace(); } }
(2)FTP断开
点击“断开”按钮,会停止上传线程,停止下载线程,清空任务队列,清除FTP资源表格内容,清除本地面板的队列等,断开端连接模块流程图如图所示。
点击“断开”按钮,会触发com.oyp.ftp.CutLinkAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
/** * 处理断开按钮的按钮动作事件的方法 */ @Override public void actionPerformed(ActionEvent e) { try { frame.ftpPanel.stopDownThread(); // 停止下载线程 frame.localPanel.stopUploadThread(); // 停止上传线程 frame.getFtpPanel().getQueue().clear(); // 清空任务队列 frame.getFtpPanel().clearTable(); // 清除FTP资源表格内容 frame.getLocalPanel().getQueue().clear(); // 清除本地面板的队列 // 如果FTP连接对象存在,并且已经连接FTP服务器 if (frame.ftpClient != null && frame.ftpClient.serverIsOpen()) { frame.ftpClient.sendServer("quit\r\n"); // 发送断开连接的FTP协议的命令 frame.ftpClient.readServerResponse(); // 读取返回编码 frame.ftpClient = null; } // 设置上传按钮不可用 frame.localPanel.getActionMap().get("uploadAction").setEnabled( false); // 设置下载按钮不可用 frame.ftpPanel.getActionMap().get("downAction").setEnabled(false); setEnabled(false); // 设置本按钮(断开)不可用 } catch (IOException e1) { e1.printStackTrace(); } }九、本地文件管理模块的实现
首先看一下界面:
1、本地文件列表的显示功能
将本地的当前目录下所有文件显示出来,并显示文件的属性包括文件名、大小、日期、通过javax.swing.JTable()来显示具体的数据。更改当前文件目录会调用com.oyp.ftp.panel.local.LocalPanel类的listLocalFiles()方法,其主要代码如下
/** * 读取本地文件到表格的方法 */ private void listLocalFiles(File selDisk) { if (selDisk == null || selDisk.isFile()) { return; } localSelFilePathLabel.setText(selDisk.getAbsolutePath()); File[] listFiles = selDisk.listFiles(); // 获取磁盘文件列表 // 获取表格的数据模型 DefaultTableModel model = (DefaultTableModel) localDiskTable.getModel(); model.setRowCount(0); // 清除模型的内容 model.addRow(new Object[] { ".", "<DIR>", "" }); // 创建.选项 model.addRow(new Object[] { "..", "<DIR>", "" }); // 创建..选项 if (listFiles == null) { JOptionPane.showMessageDialog(this, "该磁盘无法访问"); return; } // 遍历磁盘根文件夹的内容,添加到表格中 for (File file : listFiles) { File diskFile = new DiskFile(file); // 创建文件对象 String length = file.length() + "B "; // 获取文件大小 if (file.length() > 1000 * 1000 * 1000) { // 计算文件G单位 length = file.length() / 1000000000 + "G "; } if (file.length() > 1000 * 1000) { // 计算文件M单位 length = file.length() / 1000000 + "M "; } if (file.length() > 1000) { length = file.length() / 1000 + "K "; // 计算文件K单位 } if (file.isDirectory()) { // 显示文件夹标志 length = "<DIR>"; } // 获取文件的最后修改日期 String modifDate = new Date(file.lastModified()).toLocaleString(); if (!file.canRead()) { length = "未知"; modifDate = "未知"; } // 将单个文件的信息添加到表格的数据模型中 model.addRow(new Object[] { diskFile, length, modifDate }); } localDiskTable.clearSelection(); // 取消表格的选择项 }
2、本地文件列表的刷新功能
点击“刷新”按钮,会触发com.oyp.ftp.panel.local.RefreshAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
/** * 刷新本地资源列表的动作处理器的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { this.localPanel.refreshCurrentFolder(); // 调用管理面板的刷新方法 }上面的响应事件会调用com.oyp.ftp.panel.local.LocalPanel类的refreshCurrentFolder()方法,其具体代码如下
/** * 刷新指定文件夹的方法 */ void refreshFolder(File file) { listLocalFiles(file); } /** * 刷新本地当前文件夹的方法 */ public void refreshCurrentFolder() { final File file = getCurrentFolder(); // 获取当前文件夹 Runnable runnable = new Runnable() { // 创建新的线程 public void run() { listLocalFiles(file); // 重载当前文件夹的列表到表格中 } }; //导致 runnable 的 run 方法在 EventQueue 的指派线程上被调用。 SwingUtilities.invokeLater(runnable); // 在事件线程中调用该线程对象 }
3、 新建本地文件夹功能
点击“新建文件夹”按钮,会触发com.oyp.ftp.panel.local.CreateFolderAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,填写要新建的文件夹名称,选择“确定”,“取消”按钮结束。其主要代码如下
/** * 创建文件夹按钮的动作处理器的动作事件的方法 */ @Override public void actionPerformed(ActionEvent e) { // 使用输入对话框接收用户输入的文件夹名称 String folderName = JOptionPane.showInputDialog("请输入文件夹名称:"); if (folderName == null) return; File curFolder = null; // 获取本地资源表格的当前选择行号 int selRow = localPanel.localDiskTable.getSelectedRow(); if (selRow < 0) { // 创建当前文件夹对象 curFolder = new File(localPanel.localSelFilePathLabel.getText()); } else { // 获取表格选择行的第一个单元值 Object value = localPanel.localDiskTable.getValueAt(selRow, 0); if (value instanceof File) { // 如果单元值是文件,则获取文件的上级文件夹 curFolder = (File) value; if (curFolder.getParentFile() != null) curFolder = curFolder.getParentFile(); } else // 否则根据界面的路径标签创建当前文件夹对象 curFolder = new File(localPanel.localSelFilePathLabel.getText()); } // 创建当前文件夹下的新文件夹对象 File tempFile = new File(curFolder, folderName); if (tempFile.exists()) {// 如果存在相同文件或文件夹 JOptionPane.showMessageDialog(localPanel, folderName + "创建失败,已经存在此名称的文件夹或文件。", "创建文件夹", JOptionPane.ERROR_MESSAGE);// 提示用户名称已存在 return; // 结束本方法 } if (tempFile.mkdir()) // 创建文件夹 JOptionPane.showMessageDialog(localPanel, folderName + "文件夹,创建成功。", "创建文件夹", JOptionPane.INFORMATION_MESSAGE); else JOptionPane.showMessageDialog(localPanel, folderName + "文件夹无法被创建。", "创建文件夹", JOptionPane.ERROR_MESSAGE); this.localPanel.refreshFolder(curFolder);// 刷新文件夹 }
4、删除本地文件功能
选择好要删除的文件或文件夹,点击“删除”按钮,会触发com.oyp.ftp.panel.local.DelFileAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,选择“是”,“否”,“取消”按钮结束。其主要代码如下
/** * 删除本地文件的动作处理器的处理动作事件的方法 */ public void actionPerformed(ActionEvent e) { // 获取表格选择的所有行 final int[] selRows = this.localPanel.localDiskTable.getSelectedRows(); if (selRows.length < 1) // 如果没有选择表格内容 return; // 结束该方法 int confirmDialog = JOptionPane.showConfirmDialog(localPanel, "确定要执行删除吗?"); // 用户确认是否删除 if (confirmDialog == JOptionPane.YES_OPTION) { // 如果用于同意删除 Runnable runnable = new Runnable() { // 创建线程 /** * 删除文件的递归方法 * * @param file * 要删除的文件对象 */ private void delFile(File file) { try { if (file.isFile()) { // 如果删除的是文件 boolean delete = file.delete(); // 调用删该文件的方法 if (!delete) { JOptionPane.showMessageDialog(localPanel, file .getAbsoluteFile() + "文件无法删除。", "删除文件", JOptionPane.ERROR_MESSAGE); return; } } else if (file.isDirectory()) { // 如果删除的是文件夹 File[] listFiles = file.listFiles();// 获取该文件夹的文件列表 if (listFiles.length > 0) { for (File subFile : listFiles) { delFile(subFile); // 调用递归方法删除该列表的所有文件或文件夹 } } boolean delete = file.delete();// 最后删除该文件夹 if (!delete) { // 如果成功删除 JOptionPane.showMessageDialog(localPanel, file .getAbsoluteFile() + "文件夹无法删除。", "删除文件", JOptionPane.ERROR_MESSAGE); return; // 返回方法的调用处 } } } catch (Exception ex) { Logger.getLogger(LocalPanel.class.getName()).log( Level.SEVERE, null, ex); } } /** * 线程的主体方法 * * @see java.lang.Runnable#run() */ public void run() { File parent = null; // 遍历表格的选择内容 for (int i = 0; i < selRows.length; i++) { // 获取每个选择行的第一列单元内容 Object value = DelFileAction.this.localPanel.localDiskTable .getValueAt(selRows[i], 0); // 如果该内容不是DiskFile类的实例对象 if (!(value instanceof DiskFile)) continue; // 结束本次循环 DiskFile file = (DiskFile) value; if (parent == null) parent = file.getParentFile(); // 获取选择文件的上级文件夹 if (file != null) { delFile(file); // 调用递归方法删除选择内容 } } // 调用refreshFolder方法刷新当前文件夹 DelFileAction.this.localPanel.refreshFolder(parent); JOptionPane.showMessageDialog(localPanel, "删除成功。"); } }; new Thread(runnable).start(); // 创建并启动这个线程 } }
5、重命名本地文件功能
选择好要重命名的文件或文件夹,点击“重命名”按钮,会触发com.oyp.ftp.panel.local.RennameAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
/** * 重命名动作的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { // 获取本地资源表格的选择行号 int selRow = this.localPanel.localDiskTable.getSelectedRow(); if (selRow < 0) return; // 获取选择行的第一个单元值 Object value = this.localPanel.localDiskTable.getValueAt(selRow, 0); if (!(value instanceof File)) return; // 将该单元值转换为File类的对象 File file = (File) value; // 使用对话框接收用户如入的新文件名 String fileName = JOptionPane .showInputDialog("请输入新文件名", file.getName()); if (fileName == null) return; // 创建新名称的文件 File renFile = new File(file.getParentFile(), fileName); boolean isRename = file.renameTo(renFile); // 将原文件重命名 // 刷新文件夹 this.localPanel.refreshFolder(file.getParentFile()); if (isRename) { JOptionPane.showMessageDialog(this.localPanel, "重命名为" + fileName + "成功。"); } else { JOptionPane.showMessageDialog(this.localPanel, "无法重命名为" + fileName + "。", "文件重命名", JOptionPane.ERROR_MESSAGE); } }十、远程文件管理模块的实现
首先看一下界面:
1、远程FTP服务器端的文件列表的显示
将远程的当前目录下所有文件显示出来,并显示文件的属性包括文件名、大小、日期、通过javax.swing.JTable()来显示具体的数据。更改当前文件目录会调用com.oyp.ftp.panel.ftp.FtpPanel类的listFtpFiles(final TelnetInputStream list)方法,其主要代码如下
/** * 读取FTP文件到表格的方法 * @param list * 读取FTP服务器资源列表的输入流 */ public synchronized void listFtpFiles(final TelnetInputStream list) { // 获取表格的数据模型 final DefaultTableModel model = (DefaultTableModel) ftpDiskTable .getModel(); model.setRowCount(0); // 创建一个线程类 Runnable runnable = new Runnable() { public synchronized void run() { ftpDiskTable.clearSelection(); try { String pwd = getPwd(); // 获取FTP服务器的当前文件夹 model.addRow(new Object[] { new FtpFile(".", pwd, true), "", "" }); // 添加“.”符号 model.addRow(new Object[] { new FtpFile("..", pwd, true), "", "" }); // 添加“..”符号 byte[]names=new byte[2048]; int bufsize=0; bufsize=list.read(names, 0, names.length); int i=0,j=0; while(i<bufsize){ //字符模式为10,二进制模式为13 // if (names[i]==10) { if (names[i]==13) { //获取字符串 -rwx------ 1 user group 57344 Apr 18 05:32 腾讯电商2013实习生招聘TST推荐模板.xls //文件名在数据中开始做坐标为j,i-j为文件名的长度,文件名在数据中的结束下标为i-1 String fileMessage = new String(names,j,i-j); if(fileMessage.length() == 0){ System.out.println("fileMessage.length() == 0"); break; } //按照空格将fileMessage截为数组后获取相关信息 // 正则表达式 \s表示空格,{1,}表示1一个以上 if(!fileMessage.split("\\s+")[8].equals(".") && !fileMessage.split("\\s+")[8].equals("..")){ /**文件大小*/ String sizeOrDir=""; if (fileMessage.startsWith("d")) {//如果是目录 sizeOrDir="<DIR>"; }else if (fileMessage.startsWith("-")) {//如果是文件 sizeOrDir=fileMessage.split("\\s+")[4]; } /**文件名*/ String fileName=fileMessage.split("\\s+")[8]; /**文件日期*/ String dateStr =fileMessage.split("\\s+")[5] +" "+fileMessage.split("\\s+")[6]+" " +fileMessage.split("\\s+")[7]; FtpFile ftpFile = new FtpFile(); // 将FTP目录信息初始化到FTP文件对象中 ftpFile.setLastDate(dateStr); ftpFile.setSize(sizeOrDir); ftpFile.setName(fileName); ftpFile.setPath(pwd); // 将文件信息添加到表格中 model.addRow(new Object[] { ftpFile, ftpFile.getSize(), dateStr }); } // j=i+1;//上一次位置为字符模式 j=i+2;//上一次位置为二进制模式 } i=i+1; } list.close(); } catch (IOException ex) { Logger.getLogger(FTPClientFrame.class.getName()).log( Level.SEVERE, null, ex); } } }; if (SwingUtilities.isEventDispatchThread()) // 启动线程对象 runnable.run(); else SwingUtilities.invokeLater(runnable); }
2、刷新远程FTP服务器端的文件列表
点击“刷新”按钮,会触发com.oyp.ftp.panel.ftp.RefreshAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
/** 刷新按钮的动作处理器动作的事件处理方法 **/ @Override public void actionPerformed(ActionEvent e) { ftpPanel.refreshCurrentFolder(); // 调用刷新FTP资源列表的方法 }上面的响应事件会调用com.oyp.ftp.panel.ftp.FtpPanel类的refreshCurrentFolder()方法,其主要代码如下
/** 刷新FTP资源管理面板的当前文件夹**/ public void refreshCurrentFolder() { try { // 获取服务器文件列表 TelnetInputStream list = ftpClient.list(); listFtpFiles(list); // 调用解析方法 } catch (IOException e) { e.printStackTrace(); } }3、新建远程FTP服务器端的文件夹
点击“新建文件夹”按钮,会触发com.oyp.ftp.panel.ftp.CreateFolderAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,填写要新建的文件夹名称,选择“确定”,“取消”按钮结束。其主要代码如下
/** * 创建文件夹的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { // 接收用户输入的新建文件夹的名称 String folderName = JOptionPane.showInputDialog("请输入文件夹名称:"); if (folderName == null) return; int read = -1; try { // 发送创建文件夹的命令 ftpPanel.ftpClient.sendServer("MKD " + folderName + "\r\n"); // 读取FTP服务器的命令返回码 read = ftpPanel.ftpClient.readServerResponse(); } catch (IOException e1) { e1.printStackTrace(); } if (read == 257) {// 如果返回码等于257(路径名建立完成) // 提示文件夹创建成功 JOptionPane.showMessageDialog(ftpPanel, folderName + "文件夹,创建成功。", "创建文件夹", JOptionPane.INFORMATION_MESSAGE); }else{ // 否则 提示用户该文件夹无法创建 JOptionPane.showMessageDialog(ftpPanel, folderName + "文件夹无法被创建。", "创建文件夹", JOptionPane.ERROR_MESSAGE); } this.ftpPanel.refreshCurrentFolder(); }4、 删除远程FTP服务器端的文件
选择好要删除的文件或文件夹,点击“删除”按钮,会触发com.oyp.ftp.panel.ftp.DelFileAction类的actionPerformed(ActionEvent e)方法,然后弹出一个对话框,选择“是”,“否”,“取消”按钮结束。其主要代码如下
public void actionPerformed(ActionEvent e) { // 获取显示FTP资源列表的表格组件当前选择的所有行 final int[] selRows = ftpPanel.ftpDiskTable.getSelectedRows(); if (selRows.length < 1) return; int confirmDialog = JOptionPane.showConfirmDialog(ftpPanel, "确定要删除吗?"); if (confirmDialog == JOptionPane.YES_OPTION) { Runnable runnable = new Runnable() { /** * 删除服务器文件的方法 * @param file - 文件名称 */ private void delFile(FtpFile file) { FtpClient ftpClient = ftpPanel.ftpClient; // 获取ftpClient实例 try { if (file.isFile()) { // 如果删除的是文件 ftpClient.sendServer("DELE " + file.getName() + "\r\n"); // 发送删除文件的命令 ftpClient.readServerResponse(); // 接收返回编码 } else if (file.isDirectory()) { // 如果删除的是文件夹 ftpClient.cd(file.getName()); // 进入到该文件夹 TelnetInputStream telnetInputStream=ftpClient.list(); byte[]names=new byte[2048]; int bufsize=0; bufsize=telnetInputStream.read(names, 0, names.length); int i=0,j=0; while(i<bufsize){ //字符模式为10,二进制模式为13 // if (names[i]==10) { if (names[i]==13) { //获取字符串 -rwx------ 1 user group 57344 Apr 18 05:32 腾讯电商2013实习生招聘TST推荐模板.xls //文件名在数据中开始做坐标为j,i-j为文件名的长度,文件名在数据中的结束下标为i-1 String fileMessage = new String(names,j,i-j); if(fileMessage.length() == 0){ System.out.println("fileMessage.length() == 0"); break; } //按照空格将fileMessage截为数组后获取相关信息 // 正则表达式 \s表示空格,{1,}表示1一个以上 if(!fileMessage.split("\\s+")[8].equals(".") && !fileMessage.split("\\s+")[8].equals("..")){ /**文件大小*/ String sizeOrDir=""; if (fileMessage.startsWith("d")) {//如果是目录 sizeOrDir="<DIR>"; }else if (fileMessage.startsWith("-")) {//如果是文件 sizeOrDir=fileMessage.split("\\s+")[4]; } /**文件名*/ String fileName=fileMessage.split("\\s+")[8]; /**文件日期*/ String dateStr =fileMessage.split("\\s+")[5] +fileMessage.split("\\s+")[6] +fileMessage.split("\\s+")[7]; FtpFile ftpFile = new FtpFile(); // 将FTP目录信息初始化到FTP文件对象中 ftpFile.setLastDate(dateStr); ftpFile.setSize(sizeOrDir); ftpFile.setName(fileName); ftpFile.setPath(file.getAbsolutePath()); // 递归删除文件或文件夹 delFile(ftpFile); } // j=i+1;//上一次位置为字符模式 j=i+2;//上一次位置为二进制模式 } i=i+1; } ftpClient.cdUp(); // 返回上层文件夹 ftpClient.sendServer("RMD " + file.getName() + "\r\n"); // 发送删除文件夹指令 ftpClient.readServerResponse(); // 接收返回码 } } catch (Exception ex) { Logger.getLogger(LocalPanel.class.getName()).log( Level.SEVERE, null, ex); } } /** * 线程的主体方法 */ public void run() { // 遍历显示FTP资源的表格的所有选择行 for (int i = 0; i < selRows.length; i++) { // 获取每行的第一个单元值,并转换为FtpFile类型 final FtpFile file = (FtpFile) ftpPanel.ftpDiskTable .getValueAt(selRows[i], 0); if (file != null) { delFile(file); // 调用删除文件的递归方法 try { // 向服务器发删除文件夹的方法 ftpPanel.ftpClient.sendServer("RMD " + file.getName() + "\r\n"); // 读取FTP服务器的返回码 ftpPanel.ftpClient.readServerResponse(); } catch (IOException e) { e.printStackTrace(); } } } // 刷新FTP服务器资源列表 DelFileAction.this.ftpPanel.refreshCurrentFolder(); JOptionPane.showMessageDialog(ftpPanel, "删除成功。"); } }; new Thread(runnable).start(); } }5、重命名远程FTP服务器端的文件
选择好要重命名的文件或文件夹,点击“重命名”按钮,会触发com.oyp.ftp.panel.ftp.RenameAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
/** * 重命名FTP文件的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { // 获取显示FTP资源的表格当前选择行号 int selRow = ftpPanel.ftpDiskTable.getSelectedRow(); if (selRow < 0) return; // 获取当前行的第一个表格单元值,并转换成FtpFile类型的对象 FtpFile file = (FtpFile) ftpPanel.ftpDiskTable.getValueAt(selRow, 0); // 使用对话框接收用户输入的新文件或文件夹名称 String newName = JOptionPane.showInputDialog(ftpPanel, "请输入新名称。"); if (file.getName().equals(".") || file.getName().equals("..") || newName == null) return; try { // 向服务器发送重命名的指令 ftpPanel.ftpClient.sendServer("RNFR " + file.getName() + "\r\n"); //对旧路径重命名 ftpPanel.ftpClient.readServerResponse(); ftpPanel.ftpClient.sendServer("RNTO " + newName + "\r\n"); //对新路径重命名 ftpPanel.ftpClient.readServerResponse(); ftpPanel.refreshCurrentFolder(); // 刷新当前文件夹 } catch (IOException e1) { e1.printStackTrace(); } }十一、上传下载管理模块的实现
1、上传本地文件或文件夹到远程FTP服务器端的功能。
当用户在本地文件列表中选择想要上传的文件后,点击上传按钮,将本机上指定的文件上传到FTP服务器当前展现的目录,下图为上传子模块流程图
选择好要上传的文件或文件夹,点击“上传”按钮,会触发com.oyp.ftp.panel.local.UploadAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
/** * 上传文件动作的事件处理方法 */ public void actionPerformed(java.awt.event.ActionEvent evt) { // 获取用户选择的多个文件或文件夹 int[] selRows = this.localPanel.localDiskTable.getSelectedRows(); if (selRows.length < 1) { JOptionPane.showMessageDialog(this.localPanel, "请选择上传的文件或文件夹"); return; } // 获取FTP服务器的当前路径 String pwd = this.localPanel.frame.getFtpPanel().getPwd(); // 创建FTP当前路径的文件夹对象 FtpFile ftpFile = new FtpFile("", pwd, true); // 遍历本地资源的表格 for (int i = 0; i < selRows.length; i++) { Object valueAt = this.localPanel.localDiskTable.getValueAt( selRows[i], 0); // 获取表格选择行的第一列数据 if (valueAt instanceof DiskFile) { final DiskFile file = (DiskFile) valueAt; // 获取本地面板类中的队列,该队列是LinkedList类的实例对象 Queue<Object[]> queue = this.localPanel.queue; queue.offer(new Object[] { file, ftpFile });// 执行offer方法向队列尾添加对象 } } }在com.oyp.ftp.panel.local.UploadThread线程类的run()方法,会判断上传队列是否有对象,如果有则调用其copyFile(File file, FtpFile ftpFile)方法实现上传文件的功能,上传完后刷新远程FTP文件管理的面板。其run()方法主要代码如下
/** * 线程的主体方法 */ public void run() { // 线程的主体方法 while (conRun) { try { Thread.sleep(1000); // 线程休眠1秒 Queue<Object[]> queue = localPanel.queue; // 获取本地面板的队列对象 queueValues = queue.peek(); // 获取队列首的对象 if (queueValues == null) { // 如果该对象为空 continue; // 进行下一次循环 } File file = (File) queueValues[0]; // 获取队列中的本队文件对象 FtpFile ftpFile = (FtpFile) queueValues[1]; // 获取队列中的FTP文件对象 if (file != null) { selPath = file.getParent(); copyFile(file, ftpFile); // 调用递归方法上传文件 FtpPanel ftpPanel = localPanel.frame.getFtpPanel(); ftpPanel.refreshCurrentFolder(); // 刷新FTP面板中的资源 } Object[] args = queue.peek(); // 判断队列顶是否为处理的上一个任务。 if (queueValues == null || args == null || !queueValues[0].equals(args[0])) { continue; } queue.remove(); // 移除队列首元素 } catch (Exception e) { e.printStackTrace(); } } }其中调用的copyFile(File file, FtpFile ftpFile)方法代码如下
/** * 上传线程的递归方法,上传文件夹的所有子文件夹和内容 * @param file * - FTP文件对象 * @param localFolder * - 本地文件夹对象 */ private void copyFile(File file, FtpFile ftpFile) { // 递归遍历文件夹的方法 // 判断队列面板是否执行暂停命令 while (localPanel.frame.getQueuePanel().isStop()) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } Object[] args = localPanel.queue.peek(); // 判断队列顶是不是上一个处理的任务。 if (queueValues == null || args == null || !queueValues[0].equals(args[0])) return; try { // System.out.println("selPath:"+selPath); path = file.getParentFile().getPath().replace(selPath, ""); // System.out.println("path:"+path); ftpFile.setName(path.replace("\\", "/")); path = ftpFile.getAbsolutePath(); // System.out.println("ftpFile.getAbsolutePath():"+path); if (file.isFile()) { UploadPanel uploadPanel = localPanel.frame.getUploadPanel();//上传面板 String remoteFile = path + "/" + file.getName(); // 远程FTP的文件名绝对路径 // System.out.println("remoteFile:" + remoteFile); double fileLength = file.length() / Math.pow(1024, 2); ProgressArg progressArg = new ProgressArg( (int) (file.length() / 1024), 0, 0);//进度参数 String size = String.format("%.4f MB", fileLength); Object[] row = new Object[] { file.getAbsoluteFile(), size, remoteFile, ftpClient.getServer(), progressArg }; uploadPanel.addRow(row); //添加列 OutputStream put = ftpClient.put(remoteFile); // 获取服务器文件的输出流 FileInputStream fis = null; // 本地文件的输入流 try { fis = new FileInputStream(file); // 初始化文件的输入流 } catch (Exception e) { e.printStackTrace(); return; } int readNum = 0; byte[] data = new byte[1024]; // 缓存大小 while ((readNum = fis.read(data)) > 0) { // 读取本地文件到缓存 Thread.sleep(0, 30); // 线程休眠 put.write(data, 0, readNum); // 输出到服务器 progressArg.setValue(progressArg.getValue() + 1);// 累加进度条 } progressArg.setValue(progressArg.getMax()); // 结束进度条 fis.close(); // 关闭文件输入流 put.close(); // 关闭服务器输出流 } else if (file.isDirectory()) { path = file.getPath().replace(selPath, ""); ftpFile.setName(path.replace("\\", "/")); // System.out.println("Dirpath:"+path); /**将目录切换到当前FTP服务器的当前目录*/ ftpClient.cd(this.localPanel.frame.getFtpPanel().getPwd()); // /media目录 /** * 如果有创建文件夹的权限,则在当前FTP服务器的当前目录下创建文件夹 * 必须要有创建文件夹的权限,否则会报错 * path:audio ftpFile.getAbsolutePath():/media/audio remoteFile:/media/audio/梁静茹-会呼吸的痛Live.mp3 */ ftpClient.sendServer("MKD " + path + "\r\n"); //创建 /media/audio 目录 ftpClient.readServerResponse(); /*********************************************************** * 如果没有有创建文件夹的权限,则创建文件夹,因此FTP服务器的当前路径下不存在 * 那么将文件上传到此FTP服务器的当前路径下 * * 如要上传C://audio目录(目录中有 梁静茹-会呼吸的痛Live.mp3 和 林宥嘉-心酸.mp3 两个文件) * 到 FTP服务器上的 /media/ 目录下 * 因为FTP服务器上没有 /media/audio 目录,并且FTP服务器当前的目录为 /media * 所以将 C://audio目录下的文件上传到了 /media目录下 * ftpFile.getAbsolutePath():/media/audio remoteFile:/media/梁静茹-会呼吸的痛Live.mp3 remoteFile:/media/林宥嘉-心酸.mp3 */ //创建一个文件夹对象,检查该文件是否存在 File fileRemote=new File(this.localPanel.frame.getFtpPanel().getPwd()+path); //path:audio //该目录不存在 if (!fileRemote.exists()) { path=this.localPanel.frame.getFtpPanel().getPwd(); } /***********************************************************/ File[] listFiles = file.listFiles(); for (File subFile : listFiles) { Thread.sleep(0, 50); copyFile(subFile, ftpFile); } } } catch (FileNotFoundException e1) { e1.printStackTrace(); System.exit(0); // JOptionPane.showMessageDialog(localPanel, e1.getMessage()); } catch (Exception ex) { ex.printStackTrace(); } }
2、下载远程FTP服务器端的文件或文件夹到本地
当用户在远程FTP服务器文件列表中选择想要下载的文件后,点击下载按钮,将服务器上的文件下载至本机,下图为下载子模块流程图。
选择好要下载的文件或文件夹,点击“下载”按钮,会触发com.oyp.ftp.panel.ftp.DownAction类的actionPerformed(ActionEvent e)方法,其主要代码如下
/** * 下载按钮的动作处理器动作的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { // 获取FTP资源表格的所有选择行 final int[] selRows = ftpPanel.ftpDiskTable.getSelectedRows(); if (selRows.length < 1) return; // 遍历表格的所有选择行 for (int i = 0; i < selRows.length; i++) { // 获取每行的第一个单元值并转换成FtpFile类的对象 final FtpFile file = (FtpFile) ftpPanel.ftpDiskTable.getValueAt( selRows[i], 0); if (file != null) { // 获取本地资源管理面板的当前文件夹 File currentFolder = ftpPanel.frame.getLocalPanel() .getCurrentFolder(); // 把FTP文件对象和本地当前文件夹对象定义成数组添加到下载队列中 ftpPanel.queue.offer(new Object[] { file, currentFolder }); } } }在com.oyp.ftp.panel.ftp.DownThread线程类的run()方法,会判断下载队列是否有对象,如果有则调用其downFile(FtpFile file, File localFolder)方法实现上传文件的功能,上传完后刷新远程FTP文件管理的面板。其run()方法代码如下
public void run() { // 线程业务方法 while (conRun) { try { Thread.sleep(1000); ftpClient.noop(); queueValues = ftpPanel.queue.peek(); if (queueValues == null) { continue; } FtpFile file = (FtpFile) queueValues[0]; File localFolder = (File) queueValues[1]; if (file != null) { path = file.getPath(); ftpClient.cd(path); downFile(file, localFolder); path = null; ftpPanel.frame.getLocalPanel().refreshCurrentFolder(); } Object[] args = ftpPanel.queue.peek(); // 判断队列顶是否为处理的上一个任务。 if (queueValues == null || args == null || !queueValues[0].equals(args[0])) continue; ftpPanel.queue.poll(); } catch (Exception e) { e.printStackTrace(); } } }其中调用的downFile(FtpFile file, File localFolder)方法代码如下
/** * 下载线程的递归方法,用户探索FTP下载文件夹的所有子文件夹和内容 * @param file FTP文件对象 * @param localFolder 本地文件夹对象 */ private void downFile(FtpFile file, File localFolder) { // 判断队列面板是否执行暂停命令 while (ftpPanel.frame.getQueuePanel().isStop()) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } Object[] args = ftpPanel.queue.peek(); // 判断队列顶是否为处理的上一个任务。 if (queueValues == null || args == null || !queueValues[0].equals(args[0])) return; try { String ftpFileStr = file.getAbsolutePath().replaceFirst(path + "/", ""); if (file.isFile()) { // 获取服务器指定文件的输入流 TelnetInputStream ftpIs = ftpClient.get(file.getName()); if (ftpIs == null) { JOptionPane.showMessageDialog(this.ftpPanel, file.getName() + "无法下载"); return; } // 创建本地文件对象 File downFile = new File(localFolder, ftpFileStr); // 创建本地文件的输出流 FileOutputStream fout = new FileOutputStream(downFile, true); // 计算文件大小 double fileLength = file.getLongSize() / Math.pow(1024, 2); ProgressArg progressArg = new ProgressArg((int) (file .getLongSize() / 1024), 0, 0); //进度参数 String size = String.format("%.4f MB", fileLength); //"文件名", "大小", "本地文件名","主机", "状态" Object[] row = new Object[] { ftpFileStr, size, downFile.getAbsolutePath(), ftpClient.getServer(), progressArg }; DownloadPanel downloadPanel = ftpPanel.frame.getDownloadPanel(); //下载队列面板 downloadPanel.addRow(row); //添加列 byte[] data = new byte[1024]; // 定义缓存 int read = -1; while ((read = ftpIs.read(data)) > 0) { // 读取FTP文件内容到缓存 Thread.sleep(0, 30); // 线程休眠 fout.write(data, 0, read); // 将缓存数据写入本地文件 // 累加进度条 progressArg.setValue(progressArg.getValue() + 1); } progressArg.setValue(progressArg.getMax());// 结束进度条 fout.close(); // 关闭文件输出流 ftpIs.close(); // 关闭FTP文件输入流 } else if (file.isDirectory()) { // 如果下载的是文件夹 // 创建本地文件夹对象 File directory = new File(localFolder, ftpFileStr); directory.mkdirs(); // 创建本地的文件夹 ftpClient.cd(file.getName()); // 改变FTP服务器的当前路径 // 获取FTP服务器的文件列表信息 TelnetInputStream telnetInputStream=ftpClient.list(); byte[]names=new byte[2048]; int bufsize=0; bufsize=telnetInputStream.read(names, 0, names.length); int i=0,j=0; while(i<bufsize){ //字符模式为10,二进制模式为13 // if (names[i]==10) { if (names[i]==13) { //获取字符串 -rwx------ 1 user group 57344 Apr 18 05:32 腾讯电商2013实习生招聘TST推荐模板.xls //文件名在数据中开始做坐标为j,i-j为文件名的长度,文件名在数据中的结束下标为i-1 String fileMessage = new String(names,j,i-j); if(fileMessage.length() == 0){ System.out.println("fileMessage.length() == 0"); break; } //按照空格将fileMessage截为数组后获取相关信息 // 正则表达式 \s表示空格,{1,}表示1一个以上 if(!fileMessage.split("\\s+")[8].equals(".") && !fileMessage.split("\\s+")[8].equals("..")){ /**文件大小*/ String sizeOrDir=""; if (fileMessage.startsWith("d")) {//如果是目录 sizeOrDir="<DIR>"; }else if (fileMessage.startsWith("-")) {//如果是文件 sizeOrDir=fileMessage.split("\\s+")[4]; } /**文件名*/ String fileName=fileMessage.split("\\s+")[8]; FtpFile ftpFile = new FtpFile(); // 将FTP目录信息初始化到FTP文件对象中 ftpFile.setSize(sizeOrDir); ftpFile.setName(fileName); ftpFile.setPath(file.getAbsolutePath()); // 递归执行子文件夹的下载 downFile(ftpFile, localFolder); } // j=i+1;//上一次位置为字符模式 j=i+2;//上一次位置为二进制模式 } i=i+1; } ftpClient.cdUp(); // 返回FTP上级路径 } } catch (Exception ex) { ex.printStackTrace(); } }十二、辅助功能模块FTP站点管理的实现
1、FTP站点管理
点击“FTP站点管理”按钮,弹出对话框“FTP站点管理”,如下图
1) 连接站点
在FTP站点管理面板上选好要连接的站点,点击“连接”按钮,则会将主机地址、端口号、用户名好,并将密码清空,如下图
到其主要代码如下
if (command.equals("link")) { // 如果单击的是连接按钮 frame.setLinkInfo(bean); // 调用setLinkInfo()方法 dispose(); // 关闭FTP站点管理对话框 }其中调用的是com.oyp.ftp.FTPClientFrame的setLinkInfo(SiteInfoBean bean)方法,其代码如下
/** * 设置FTP连接信息的方法,由FTP站点管理器调用 */ public void setLinkInfo(SiteInfoBean bean) { serverTextField.setText(bean.getServer()); // 设置主机地址 portTextField.setText(bean.getPort() + ""); // 设置端口号 userTextField.setText(bean.getUserName()); // 设置用户名 PassField.setText(""); // 密码清空 PassField.requestFocus(); // 密码框请求焦点 }
2) 添加站点
在FTP站点管理面板上点击“添加”按钮,会产生一个新的对话框“添加FTP站点”,如下图
填写好站点名称、地址、端口号、登陆用户后点击”确定”或者”重置”按钮会触发com.oyp.ftp.panel.manager.SiteDialog类的actionPerformed(ActionEvent e)方法,其代码如下
/** * 界面按钮的事件处理方法 */ @Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); // 获取按钮的command属性 if (command.equals("ok")) { // 如果是确定按钮 try { if (dialog == null) { dispose(); return; } // 获取界面所有文本框的内容 String siteName = siteNameField.getText().trim(); String server = siteAddressField.getText().trim(); String userName = loginUserField.getText().trim(); String portStr = portField.getText().trim(); // 判断是否填写了全部文本框 if (siteName.isEmpty() || server.isEmpty() || userName.isEmpty() || portStr.isEmpty()) { JOptionPane.showMessageDialog(this, "请填写全部信息"); return; } int port = Integer.valueOf(portStr); // 创建FTP站点信息的JavaBean对象 SiteInfoBean bean = new SiteInfoBean(siteName, server, port, userName); // 如果对话框的siteBean不为空 if (siteBean != null) bean.setId(siteBean.getId()); // 设置FTP站点的ID编号 dialog.addSite(bean); // 调用父窗体的 addSite方法添加站点 dialog.loadSiteList(); // 调用父窗体的loadSiteList方法重载站点列表 dispose(); } catch (NullPointerException ex) { ex.printStackTrace(); return; } catch (NumberFormatException ex) { JOptionPane.showMessageDialog(this, "请正确填写端口号信息"); ex.printStackTrace(); return; } } if (command.equals("cancel")) { // 如果是重置按钮 if (siteBean == null) // 如果对话框的siteBean属性为空 clearInput(); // 调用清除文本框内容的方法 else // 否则 initInput(); // 初始化界面文本框内容 } }
3) 编辑站点
在FTP站点管理面板上选好要编辑的站点,点击“编辑”按钮,会产生一个新的对话框“编辑FTP站点”,如下图
编辑写好站点名称、地址、端口号、登陆用户后点击”确定”或者”重置”按钮触发com.oyp.ftp.panel.manager.SiteDialog类的actionPerformed(ActionEvent e)方法,其代码如添加站点里一样。
4) 删除站点
在FTP站点管理面板上选好要删除的站点,点击“删除”按钮,调用delSite(SiteInfoBean bean)方法,其代码如下
/** * 删除FTP站点的方法 */ public void delSite(SiteInfoBean bean) { // 从站点属性集合对象中移除指定ID编号的站点属性 siteInfo.remove(bean.getId()); try { // 获取站点属性文件的输出流 FileOutputStream out = new FileOutputStream(FILE); siteInfo.store(out, "FTP站点数据"); // 调用store方法存储站点属性 loadSiteList(); // 重新装载站点列表 } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } }
2、上传/下载任务结束后自动关机
在com.oyp.ftp.panel.queue.QueuePanel类的refreshQueue()方法里会判断任务队列是否为空以及自动关机按钮是否被按下,如果满足条件则执行系统关机命令,延迟30秒后自动关机。其代码如下
/** * 刷新队列的方法 */ private synchronized void refreshQueue() { // 如果自动关机按钮被按下并且上传和下载的队列都有任务 if (frame.getShutdownButton().isSelected() && localQueue.isEmpty() && ftpQueue.isEmpty()) { try { // 执行系统关机命令,延迟30秒钟 Runtime.getRuntime().exec("shutdown -s -t 30"); } catch (IOException e) { e.printStackTrace(); } } // 创建表格的数据模型对象 DefaultTableModel model = new DefaultTableModel(columns, 0); // 获取本地上传队列中的任务 Object[] localQueueArray = localQueue.toArray(); // 遍历本地上传任务 for (int i = 0; i < localQueueArray.length; i++) { Object[] queueValue = (Object[]) localQueueArray[i]; if (queueValue == null) continue; File localFile = (File) queueValue[0]; // 把上传队列的任务添加到表格组件的数据模型中 model.addRow(new Object[] { localFile.getAbsoluteFile(), "上传",ftpClient.getServer(), i == 0 ? "正在上传" : "等待上传" }); } // 获取下载队列的任务 Object[] ftpQueueArray = ftpQueue.toArray(); // 遍历下载队列 for (int i = 0; i < ftpQueueArray.length; i++) { Object[] queueValue = (Object[]) ftpQueueArray[i]; if (queueValue == null) continue; FtpFile ftpFile = (FtpFile) queueValue[0]; // 把下载队列的任务添加到表格组件的数据模型中 model.addRow(new Object[] { ftpFile.getAbsolutePath(), "下载", ftpClient.getServer(), i == 0 ? "正在下载" : "等待下载" }); } queueTable.setModel(model); // 设置表格使用本方法的表格数据模型 }
3、软件系统化托盘
当点击最小化软件后,系统就会变成一个生成系统推盘,点击系统托盘右键会有“显示主窗体”和“退出”两个菜单,如下图
以下是初始化系统托盘的代码,如下
/** * 初始化系统托盘的方法 */ private void initSystemTray() { if (SystemTray.isSupported()) systemTray = SystemTray.getSystemTray(); TrayIcon trayIcon = new TrayIcon(icon.getImage()); PopupMenu popupMenu = new PopupMenu("托盘菜单"); // 创建显示主窗体菜单项 MenuItem showMenuItem = new MenuItem("显示主窗体"); showMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { FTPClientFrame.this.setExtendedState(JFrame.NORMAL); FTPClientFrame.this.setVisible(true); } }); // 创建退出菜单项 MenuItem exitMenuItem = new MenuItem("退出"); exitMenuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { System.exit(0); } }); popupMenu.add(showMenuItem); popupMenu.addSeparator(); popupMenu.add(exitMenuItem); trayIcon.setPopupMenu(popupMenu); try { systemTray.add(trayIcon); } catch (AWTException e) { e.printStackTrace(); } }源码地址:
http://download.csdn.net/detail/u013510614/9422600
配套服务器地址:
http://download.csdn.net/detail/u013510614/9422583
相关文章推荐
- SpringMVC之HandlerMethodArgumentResolver和<mvc:argument-resolvers>
- spring 深入reading
- SpringMVC返回json数据的三种方式
- jdk目录详解及其使用方法
- The Java™ Tutorials — Generics :Wildcard Capture and Helper Methods 通配符匹配和辅助方法
- java 规范的代码结构
- Java JDBC(2)
- 使用Ant运行JUnit测试用例时,报java.lang.ClassNotFoundException: org.hamcrest.SelfDescribing的一种解决方案
- eclipse中安装svn插件
- Java中关于i=i++的正确解释方法
- Java enum
- R语言RJava安装步骤
- Qt持久性对象进行序列化(同时比较了MFC与Java的方法)
- java自动装箱拆箱
- Spring的aop简单示例
- Twitter Snowflake 的Java实现
- Java设计模式(七)----装饰模式
- Java位运算总结:位运算用途广泛
- GC专家系列2:Java 垃圾回收的监控
- Java反射机制