Java语言实现简单FTP软件------>上传下载管理模块的实现(十一)

简介: 1、上传本地文件或文件夹到远程FTP服务器端的功能。 当用户在本地文件列表中选择想要上传的文件后,点击上传按钮,将本机上指定的文件上传到FTP服务器当前展现的目录,下图为上传子模块流程图 选择好要上传的文件或文件夹,点击“上传”按钮,会触发com.

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();
		}
	}





==================================================================================================

  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址http://blog.csdn.net/ouyang_peng

==================================================================================================


相关文章
|
1天前
|
JavaScript Java 测试技术
基于Java的库存管理软件的设计与实现(源码+lw+部署文档+讲解等)
基于Java的库存管理软件的设计与实现(源码+lw+部署文档+讲解等)
13 1
|
1天前
|
前端开发 Java Go
开发语言详解(python、java、Go(Golong)。。。。)
开发语言详解(python、java、Go(Golong)。。。。)
|
1天前
|
人工智能 前端开发 Java
Java语言开发的AI智慧导诊系统源码springboot+redis 3D互联网智导诊系统源码
智慧导诊解决盲目就诊问题,减轻分诊工作压力。降低挂错号比例,优化就诊流程,有效提高线上线下医疗机构接诊效率。可通过人体画像选择症状部位,了解对应病症信息和推荐就医科室。
27 10
|
6天前
|
Java Android开发 C++
Kotlin vs Java:选择最佳语言进行安卓开发
【4月更文挑战第13天】Java曾是安卓开发的主流语言,但Kotlin的崛起改变了这一局面。Google在2017年支持Kotlin,引发两者优劣讨论。Java以其成熟稳定、强大生态和跨平台能力占优,但代码冗长、开发效率低和语言特性过时是短板。Kotlin则以简洁语法、空安全设计和高度兼容Java脱颖而出,但社区和生态系统仍在发展中,可能存在学习曲线和性能问题。选择语言应考虑项目需求、团队熟悉度、维护性、性能和生态系统。无论选择哪种,理解其差异并适应新技术至关重要。
|
17天前
|
Java
Java语言打印九九乘法表(详解)
Java语言打印九九乘法表(详解)
15 1
Java语言打印九九乘法表(详解)
时间轮-Java实现篇
在前面的文章《[时间轮-理论篇](https://developer.aliyun.com/article/910513)》讲了时间轮的一些理论知识,然后根据理论知识。我们自己来实现一个简单的时间轮。
|
8天前
|
安全 算法 Java
深入理解Java并发编程:线程安全与性能优化
【4月更文挑战第11天】 在Java中,高效的并发编程是提升应用性能和响应能力的关键。本文将探讨Java并发的核心概念,包括线程安全、锁机制、线程池以及并发集合等,同时提供实用的编程技巧和最佳实践,帮助开发者在保证线程安全的前提下,优化程序性能。我们将通过分析常见的并发问题,如竞态条件、死锁,以及如何利用现代Java并发工具来避免这些问题,从而构建更加健壮和高效的多线程应用程序。
|
1天前
|
安全 Java
java多线程(一)(火车售票)
java多线程(一)(火车售票)
|
1天前
|
安全 Java 调度
Java并发编程:深入理解线程与锁
【4月更文挑战第18天】本文探讨了Java中的线程和锁机制,包括线程的创建(通过Thread类、Runnable接口或Callable/Future)及其生命周期。Java提供多种锁机制,如`synchronized`关键字、ReentrantLock和ReadWriteLock,以确保并发访问共享资源的安全。此外,文章还介绍了高级并发工具,如Semaphore(控制并发线程数)、CountDownLatch(线程间等待)和CyclicBarrier(同步多个线程)。掌握这些知识对于编写高效、正确的并发程序至关重要。
|
1天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。