大学四年,我用这个自己写的一个FTP,吊打运维面试官

在这里插入图片描述

在这里插入图片描述

S-FTPClient

S-FTPClient(Swing,Java,Socket,FTP,加密算法)

一、 问题描述

  • 实现一个图形用户界面的FTP客户端,保证文件的安全传输和存储。
  • 客户端能够发出各种操作命令;
  • 实现:conn(连接)list(列示文件)retr(下载)store(上载)的功能;
  • 使用一种加密算法,在文件上载前进行加密文件密文的形式传输保存在FTP服务器上,设计客户端的密钥管理机制

二、基本要求

  • 1、实现一个图形用户界面的FTP客户端,保证文件的安全传输和存储。

  • 2、功能:

    • 2.1 配置使用IIS的FTP服务器;
    • 2.2 客户端发出各种操作命令;至少实现conn(连接)list(列示文件)retr(下载)store(上载)的功能;
  • 3、选择你学过的加密算法,在文件上载前进行加密文件以密文的形式传输和保存在FTP服务器上,设计客户端的密钥管理机制。

  • 4、接收服务器的操作结果,如显示连接状态,对下载的文件进行解密等。

  • 5、用户界面:客户端界面用户可以设置远程主机名用户和密码显示远程文件列表显示本地文件的列表操作命令可以采用菜单按钮及弹出菜单来实现显示操作状态(操作是否成功状态文件操作的进度等)。

三、设计思想

FTP客户端是建立在Java的Swing技术上,首先设计好大概的FTP客户端的界面模型,然后利用Java的Swing技术绘制好FTP客户端的界面,最后利用Socket技术设计操作FTP客户端的命令等。

用户可以通过它把自己机器与世界各地所有运行 FTP协议的服务器相连,访问服务器上的资源和信息。

当启动 FTP 从远程计算机拷贝文件时,事实上启动了两个程序:一个本地机器上FTP客户端程序,它向 FTP服务器提出拷贝文件的请求。另一个是启动在远程计算机的上的 FTP服务器程序,它响应请求把你指定的文件传送到你的计算机中。

在典型的 FTP 会话过程中,用户一般在本地主机前进行同远程主机之间的文件传输。

为了能够访问远程账户,用户必须提供用户标识和密码。在通过了身份验证之后,用户就可以在本地主机和远程主机之间传输文件了。用户通过 FTP 的用户代理与 FTP 进行交互。用户首先需要远程提供主机名或 IP 地址,以便本地 FTP的客户进程能够同远程主机上的 FTP服务器进程建立连接。然后,用户提供其标识和密码。一旦验证通过,用户即可在两个系统之间传输文件。

FTP使用两个并行的 TCP协议来传输文件,一个称为控制连接,另一个称为数据连接。控制连接用来在两台主机之间传输控制信息,如用户标识、密码、操作远程主机文件目录的命令、发送和取向文件的命令等。而数据连接则真正用来发送文件。

FTP的控制和数据连接如下图所示。

在这里插入图片描述

当用户启动一次与远程主机的 FTP会话时,FTP首先建立一个 TCP连接到 FTP服务器的 21号端口。

FTP的客户端则通过该连接发送用户标识和密码等,客户端还可以通过该连接发送命令以改变远程系统的当前工作目录。当用户要求传送文件时,FTP服务器则在其 20号端口上建立一个数据连接,FTP在该连接上传送完毕一个文件后会立即断开该连接。

如果再一次 FTP会话过程中需要传送另一个文件,FTP服务器则会建立另一个连接。在整个 FTP会话过程中,控制连接是始终保持的,而数据连接则会随着文件的传输不断的打开和关闭。

综上所述需要根据FTP的控制连接和数据连接这整个流程完成对FTP的操作。

四、系统结构

经过需求分析后,决定此FTP客户端分本地文件加载系统模块、连接数据模块、断开连接模块、下载模块、刷新模块、删除模块等几项关键的模块。

本程序的系统结构图如下:
在这里插入图片描述

五、FTP客户端结构

(一)本地文件系统加载模块:

当页面初始化的时候用来加载本地文件系统的,可以让用户更为直观的,方便的,快捷的选择本地相应的文件进行浏览、上传等操作。本地文件系统加载模块如图所示:

在这里插入图片描述

(二)本地文件系统加载模块

连接模块:

连接模块是在图形界面下用户交互与FTP服务器建立连接的一个核心的模块功能。可以使用规定好的IP地址进行连接,IP无论是外网还是内网都可以进行连接。支持不同端口的FTP服务器进行连接,这个设计是为了方便不同端口的FTP。支持匿名用户和FTP普通用户登录客户端。全称由用户决定,随时可以输入的。连接模块如图所示:

在这里插入图片描述

断开模块:
断开模块是方便用户断开已有的连接而设计的。此功能图和连接模块的图一样。

下载模块:
下载模块是为用户下载FTP服务器的文件而设计的。当用户想要下载的时候,必须要选择上想要下载的文件。不然是不能下载的,界面设计如下:

在这里插入图片描述

刷新、删除模块如上图所示。

(三)程序流程

本系统主要的流程如下:

从运行程序到登录FTP服务器到拉取FTP服务器目录

上传文件流程图:

在这里插入图片描述

下载数据流程图:
在这里插入图片描述

加密流程:
在这里插入图片描述

解密过程:
在这里插入图片描述

(四)测试

1、测试环境

使用IIS搭建的本地的FTP服务器。
在这里插入图片描述

2、测试数据

第一种:
只输入FTP服务器的连接地址:127.0.0.1
第二种:
输入地址:127.0.0.1
端口:21
账号:zhenghui
密码:8042965
然后进行上传本地的文件和下载FTP服务器上的文件

(五)测试情况

第一种情况:
在这里插入图片描述

选择文件上传
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

下载:

在这里插入图片描述

下载到特定位置:

在这里插入图片描述

下载后的文件:
在这里插入图片描述

选择一个进行删除
在这里插入图片描述

删除后会立马刷新列表

在这里插入图片描述

六、核心代码

(一)初始化ftp服务器

/**
	 * 初始化ftp服务器
	 */
	public void initFtpClient() {
		System.out.println(hostname+" ," +port+","+username+","+password);
		ftpClient = new FTPClient();
//		ftpClient.setControlEncoding("utf-8");
		ftpClient.setControlEncoding("GBK");
		try {
			System.out.println("connecting...ftp服务器:" + this.hostname + ":" + this.port);
			ftpClient.connect(hostname, port); // 连接ftp服务器
			ftpClient.login(username, password); // 登录ftp服务器
			
			int replyCode = ftpClient.getReplyCode(); // 是否成功登录服务器
			if (!FTPReply.isPositiveCompletion(replyCode)) {
				System.out.println("connect failed...ftp服务器:" + this.hostname + ":" + this.port);
			}
			System.out.println("connect successfu...ftp服务器:" + this.hostname + ":" + this.port);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

(二)上传文件

/**
	 * 上传文件
	 * 
	 * @param pathname
	 *            ftp服务保存地址
	 * @param fileName
	 *            上传到ftp的文件名
	 * @param originfilename
	 *            待上传文件的名称(绝对地址) *
	 * @return
	 */
	public boolean uploadFile(String pathname, String fileName, String originfilename) {
		InputStream inputStream = null;
		try {
			System.out.println("开始上传文件");
			inputStream = new FileInputStream(new File(originfilename));
			initFtpClient();
			ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
			CreateDirecroty(pathname);
			ftpClient.makeDirectory(pathname);
			ftpClient.changeWorkingDirectory(pathname);
			// 每次数据连接之前,ftp client告诉ftp server开通一个端口来传输数据
			ftpClient.enterLocalPassiveMode();
			// 观察是否真的上传成功
			boolean storeFlag = ftpClient.storeFile(fileName, inputStream);
			System.err.println("storeFlag==" + storeFlag);
			inputStream.close();
			ftpClient.logout();
			System.out.println("上传文件成功");
		} catch (Exception e) {
			System.out.println("上传文件失败");
			e.printStackTrace();
		} finally {
			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != inputStream) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return true;
	}
 
	/**
	 * 上传文件
	 * 
	 * @param pathname
	 *            ftp服务保存地址
	 * @param fileName
	 *            上传到ftp的文件名
	 * @param inputStream
	 *            输入文件流
	 * @return
	 */
	public boolean uploadFile(String pathname, String fileName, InputStream inputStream) {
		try {
			System.out.println("开始上传文件");
			initFtpClient();
			ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
			CreateDirecroty(pathname);
			ftpClient.makeDirectory(pathname);
			ftpClient.changeWorkingDirectory(pathname);
			ftpClient.storeFile(fileName, inputStream);
			inputStream.close();
			ftpClient.logout();
			System.out.println("上传文件成功");
		} catch (Exception e) {
			System.out.println("上传文件失败");
			e.printStackTrace();
		} finally {
			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != inputStream) {
				try {
					inputStream.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return true;
	}

(三)切换目录

// 改变目录路径
	public boolean changeWorkingDirectory(String directory) {
		boolean flag = true;
		try {
			flag = ftpClient.changeWorkingDirectory(directory);
			if (flag) {
				System.out.println("进入文件夹" + directory + " 成功!");
 
			} else {
				System.out.println("进入文件夹" + directory + " 失败!开始创建文件夹");
			}
		} catch (IOException ioe) {
			ioe.printStackTrace();
		}
		return flag;
	}

(四)切换目录

	// 创建多层目录文件,如果有ftp服务器已存在该文件,则不创建,如果无,则创建
	public boolean CreateDirecroty(String remote) throws IOException {
		boolean success = true;
		String directory = remote + "/";
		// 如果远程目录不存在,则递归创建远程服务器目录
		if (!directory.equalsIgnoreCase("/") && !changeWorkingDirectory(new String(directory))) {
			int start = 0;
			int end = 0;
			if (directory.startsWith("/")) {
				start = 1;
			} else {
				start = 0;
			}
			end = directory.indexOf("/", start);
			String path = "";
			String paths = "";
			while (true) {
				String subDirectory = new String(remote.substring(start, end).getBytes("GBK"), "iso-8859-1");
				path = path + "/" + subDirectory;
				if (!existFile(path)) {
					if (makeDirectory(subDirectory)) {
						changeWorkingDirectory(subDirectory);
					} else {
						System.out.println("创建目录[" + subDirectory + "]失败");
						changeWorkingDirectory(subDirectory);
					}
				} else {
					changeWorkingDirectory(subDirectory);
				}
 
				paths = paths + "/" + subDirectory;
				start = end + 1;
				end = directory.indexOf("/", start);
				// 检查所有目录是否创建完毕
				if (end <= start) {
					break;
				}
			}
		}
		return success;
	}

(五)判断ftp服务器文件是否存在

// 判断ftp服务器文件是否存在
	public boolean existFile(String path) throws IOException {
		boolean flag = false;
		FTPFile[] ftpFileArr = ftpClient.listFiles(path);
		if (ftpFileArr.length > 0) {
			flag = true;
		}
		return flag;
	}

(六)创建目录

// 创建目录
	public boolean makeDirectory(String dir) {
		boolean flag = true;
		try {
			flag = ftpClient.makeDirectory(dir);
			if (flag) {
				System.out.println("创建文件夹" + dir + " 成功!");
 
			} else {
				System.out.println("创建文件夹" + dir + " 失败!");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return flag;
	}

(七)下载文件

/**
	 * * 下载文件 *
	 * 
	 * @param pathname
	 *            FTP服务器文件目录 *
	 * @param filename
	 *            文件名称 *
	 * @param localpath
	 *            下载后的文件路径 *
	 * @return
	 */
	public boolean downloadFile(String pathname, String filename, String localpath) {
		boolean flag = false;
		OutputStream os = null;
		try {

			initFtpClient();
			// 切换FTP目录
			boolean changeFlag = ftpClient.changeWorkingDirectory(pathname);

 
			ftpClient.enterLocalPassiveMode();
			ftpClient.setRemoteVerificationEnabled(false);
			// 查看有哪些文件夹 以确定切换的ftp路径正确
			String[] a = ftpClient.listNames();

 
			FTPFile[] ftpFiles = ftpClient.listFiles();
			for (FTPFile file : ftpFiles) {
				if (filename.equalsIgnoreCase(file.getName())) {

					byte[] base64jiemi = ZHFileBase64Key.base64jiemi(file.getName());
	                String jiemiFileName = new String(base64jiemi);
	                String[] split = jiemiFileName.split("#SDJZDX_zhenghuiagsdahscasdqwFTP");
					

	                File localFile = new File("/" + split[0]);
					os = new FileOutputStream(localFile);
					ftpClient.retrieveFile(file.getName(), os);
					os.close();
					
					ZHFileCipherTxst.decode("/" + split[0], localpath+"/" + split[0], ZHFileCipherTxst.key);
					localFile.delete();
				}
			}
			ftpClient.logout();
			flag = true;
			System.out.println("下载文件成功");
		} catch (Exception e) {
			System.out.println("下载文件失败");
			e.printStackTrace();
		} finally {
			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (null != os) {
				try {
					os.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return flag;
	}

(八)删除文件

/**
	 * * 删除文件 *
	 * 
	 * @param pathname
	 *            FTP服务器保存目录 *
	 * @param filename
	 *            要删除的文件名称 *
	 * @return
	 */
	public boolean deleteFile(String pathname, String filename) {
		boolean flag = false;
		try {
			System.out.println("开始删除文件");
			initFtpClient();
			// 切换FTP目录
			ftpClient.changeWorkingDirectory(pathname);
			ftpClient.dele(filename);
			ftpClient.logout();
			flag = true;
			System.out.println("删除文件成功");
		} catch (Exception e) {
			System.out.println("删除文件失败");
			e.printStackTrace();
		} finally {
			if (ftpClient.isConnected()) {
				try {
					ftpClient.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return flag;
	}
 

(九)连接ftp服务器

//连接ftp服务器
	public void connect(String host, int port, String userName, String password){
		try {
			//与ftp建立连接
			socket = new Socket(host,port);
			//读
			reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
			//写
//			writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
			System.out.println("正在连接" + host+"....");
			mainForm.setFTPMessage("正在连接" + host+"....");
			
			ctrlWriter = new PrintWriter(socket.getOutputStream()); 
			
			String line = null;
			line = reader.readLine();
			System.out.println("响应:"+line);
			mainForm.setFTPMessage("响应:"+line);
			
			//发送命令,输入用户名:
			System.out.println("命令:USER "+userName);
			mainForm.setFTPMessage("命令:USER "+userName);
			
			sendCommand("USER "+userName);
			line = reader.readLine();
			System.out.println("响应:"+line);
			mainForm.setFTPMessage("响应:"+line);

			//发送命令,输入密码
			sendCommand("PASS "+password);
			line = reader.readLine();
			System.out.println("命令:PASS "+password);
			System.out.println("响应:"+line);
			mainForm.setFTPMessage("命令:PASS "+password);
			mainForm.setFTPMessage("响应:"+line);
					
		} catch (UnknownHostException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

(十)操作FTP的命令

//输入操作FTP的命令
	public void sendCommand(String com) throws IOException {
		//先判断是否连接了
		if(socket == null) {
			throw new IOException("FTP未连接");
		}
		//如果发送失败
		try {
			//发送命令
//			writer.write(com+"\r\n");
//			writer.flush();
			ctrlWriter.println(com);
			ctrlWriter.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

(十一)查询ftp服务器所有文件

//查询所有的文件
	public void  selectAll() throws IOException {
		if(socket == null) {
			throw new IOException("FTP未连接");
		}
		
		//如果发送失败
		try {
			//发送命令
			sendCommand("PWD");
			sendCommand("PASV");
			sendCommand("TYPE A");
			sendCommand("LIST");
			
			String line = null;
			while ((line=reader.readLine())!=null) {
				System.out.println(line);
			}
			
		} catch (Exception e) {
			socket = null;
			throw e;
		}
	}

(十二)向ftp服务器发送指令

//发送指令
  	public void sendOrder(String order) throws IOException {
  		sendCommand("PASV");//开启一个被动连接的
//  		sendCommand("PORT");//开启一个主动连接的
        //取得被动连接模式的端口等信息
        String  response = reader.readLine();
        System.out.println(response);
        
        int opening = response.indexOf('(');
        int closing = response.indexOf(')', opening + 1);
        String ip_port = response.substring(opening+1,closing);

        //        System.out.println(closing);
        //提取227 Entering Passive Mode (127,0,0,1,250,80).  中的IP地址和端口号
        //IP:127.0.0.1
        //端口:250*254+80
        String ip = null;
        int port = -1;
        if (closing > 0) {
            String dataLink = response.substring(opening + 1, closing);
            StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
            try {
                ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken() + "." + tokenizer.nextToken();
                port = Integer.parseInt(tokenizer.nextToken()) * 256  + Integer.parseInt(tokenizer.nextToken());
            } catch (Exception e) {
                throw new IOException("FTP接收到错误的数据链接信息: "
                        + response);
            }
        }
        
        System.out.println("上传文件的IP:"+ip + "  端口号:" + port);
        mainForm.setFTPMessage("上传文件的IP:"+ip + "  端口号:" + port);
        sendCommand("PORT "+ip_port);//发送命令

        System.out.println(reader.readLine());
        sendCommand(order);//发送命令

        String str1 = null;
        while ((str1=reader.readLine())!=null) {
        	System.out.println("str1:"+str1);
        }
  	}
//dataConnection方法 
//构造与服务器交换数据用的Socket 
//再用PORT命令将端口通知服务器 
public Socket dataConnection(String ctrlcmd,int port) { 
	 String cmd = "PORT "; // PORT存放用PORT命令传递数据的变量 
	 int i; 
	 Socket dataSocket = null;// 传送数据用Socket 
	 try { 
	  // 得到自己的地址 
	  byte[] address = InetAddress.getLocalHost().getAddress(); 
	  // 用适当的端口号构造服务器 
	  ServerSocket serverDataSocket = new ServerSocket(port,1); 
	 
	  // 利用控制用的流传送PORT命令 
	  ctrlWriter.println(cmd+""+port); 
	  ctrlWriter.flush(); 
	  // 向服务器发送处理对象命令(LIST,RETR,及STOR) 
	  ctrlWriter.println(ctrlcmd); 
	  ctrlWriter.flush(); 
	 
	  // 接受与服务器的连接 
	         
	  dataSocket = serverDataSocket.accept(); 
	  serverDataSocket.close(); 
	 } catch (Exception e) { 
	  e.printStackTrace(); 
	  System.exit(1); 
	 } 
 return dataSocket; 
}

(十三)获取FTP的数据

	//获取FTP的数据
	public void listFiles(String string, FTPTableModel ftpTableModel) throws IOException {
		    sendCommand("cwd  " + string);
	        String string1 = reader.readLine();
	        System.out.println("响应:" + string1);
	        mainForm.setFTPMessage("响应:"+ string1);
	        sendCommand("PASV");
	        
	        //取得目录信息 
	        String  response = reader.readLine();
	        String[] ip_PORT = getIP_PORT(response);
	        String ip =ip_PORT[0];
	        int port = Integer.valueOf(ip_PORT[1]);
	        
	        
	        System.out.println("listFilesIP:"+ip + "  listFiles端口号:" + port);
	        sendCommand("LIST" );
	        mainForm.setFTPMessage("listFilesIP:"+ip + "  listFiles端口号:" + port);

	        Socket dataSocket = new Socket(ip, port);
	        
	        DataInputStream dis = new DataInputStream(dataSocket.getInputStream());
	        
	        String s = "";
	        ArrayList<FileEntity> fileList = new ArrayList<FileEntity>();
	        
	        //遍历目录
	        while ((s = dis.readLine()) != null) {
	            String l = new String(s.getBytes("ISO-8859-1"), "GB2312");//转换字符类型,解决乱码问题
	            //如果是目录
	            if(l.indexOf("<DIR>")!=-1) {
	            	 //是目录的时候的字符串分割,拿到文件名的
	            	 String[] split = l.split("       <DIR>");
	            	 String string2 = "日期:"+split[0]+",文件夹,文件名:"+split[1].split("          ")[1];
	            	 String fileName = split[1].split("          ")[1];
	            	 String fileSise = "-1";
	            	 String fileType = "文件夹";
	            	 String fileUpdateDate = split[0];
	            	 //存到List里
	            	 fileList.add(new FileEntity(fileName, fileSise, fileType, fileUpdateDate));
	            }else{//如果不是目录
	           	 	String[] split = l.split("             ");
	           	    String[] split2 = split[1].trim().split(" ");
		           	 String string2 ="日期:"+split[0]+",文件,大小:"+split2[0]+",文件名:"+split2[1];
//		           	 mainForm.setFTPMessage(string2);
		           	 String fileName = split2[1];
	            	 String fileSise = String.valueOf(split2[0]);
	            	 String fileType = "文件";
	            	 String fileUpdateDate = split[0];
	            	 
		           	 //存到List里
	            	 fileList.add(new FileEntity(fileName, fileSise, fileType, fileUpdateDate));
	            }
	        }
	        
	        ftpTableModel.setData(fileList);
	        
	        
	        
	        
	        dis.close();
	        dataSocket.close();
	        System.out.println(reader.readLine());
	        System.out.println(reader.readLine());
//	        String str1 = null;
//	        while ((str1=reader.readLine())!=null) {
//			}
	        
	}

(十四)提取IP地址和端口号

	 //提取IP地址和端口号
	 public String[] getIP_PORT(String  response) throws IOException {
		 
		 String ip = null;
	     int port = -1;
	     int opening = response.indexOf('(');
	     int closing = response.indexOf(')', opening + 1);

	     if (closing > 0) {
	         String dataLink = response.substring(opening + 1, closing);
	         StringTokenizer tokenizer = new StringTokenizer(dataLink, ",");
	         try {
	             ip = tokenizer.nextToken() + "." + tokenizer.nextToken() + "."
	                     + tokenizer.nextToken() + "." + tokenizer.nextToken();
	             port = Integer.parseInt(tokenizer.nextToken()) * 256
	                     + Integer.parseInt(tokenizer.nextToken());
	         } catch (Exception e) {
	             throw new IOException("FTP接收到错误的数据链接信息: " + response);
	         }
	     }
	     
	     String [] str= {ip,String.valueOf(port)};
		 return str;
	 }

(十五)关闭所有连接

//关闭所有的连接
	public void closeAll() {
		if(socket!=null) {
			try {
				socket.close();
			} catch (IOException e) {
				System.out.println("关闭socket失败");
				e.printStackTrace();
			}
		}

		if(reader!=null) {
			try {
				reader.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		
		if(ctrlWriter!=null) {
			ctrlWriter.close();
		}
	}

七、项目下载

项目放在:
下载项目点我

发布了177 篇原创文章 · 获赞 2525 · 访问量 19万+

猜你喜欢

转载自blog.csdn.net/qq_17623363/article/details/105436592