您的位置:首页 > 编程语言 > Java开发

Java语言实现简单FTP软件

2016-01-29 20:40 639 查看
作者:欧阳鹏 欢迎转载,与人分享是进步的源泉!

转载请保留原文地址:/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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: