Apache commons.net FTPClient使用简述
本文遵守CC BY-SA 3.0。
前言:
之前写过个搭建FTP服务器的,搭建完成就要开始使用,网上大概看到过两种通过java来读取、写入,一种是用sun的包,还有一个就是apache的commons.net,前面那个没用过,就不展开了,这里主要介绍apache的这个,废话略多,开始正文。
哦也,还要补充一点,由于网上信息杂乱,好多用法都是直接到官网看API文档自己摆弄的,要是有什么问题或者土鳖的用法,欢迎拍我砖&指证,下面贴出API地址:
一、建立连接
其实比较简单,就是首先开启连接,然后输入用户名/密码,login函数会返回个boolean,根据需要使用即可。
值得说明的是,我总是感觉那个用户名和密码的明文组合真的很蛋疼,就自作主张的使用了一个Properties文件,把url/username/passwd都放进去了,当然,还有个basePath,这个后面的结合apache会用到,到时候再说。
还要注意的是9,10行的两句话,
1.setFileType(int fileType),如果没有需求上传图片的话还ok,但是要是传图片,就需要设置一下文件类型为二进制,这样上传的图片才不会报错(记得我的错误貌似是什么ASCII编码什么的。。)
2.setControlEncoding(),设置FTP的字符编码,这个函数是从FTP父类过来的。
1 public static FTPClient getFtpClient(Properties properties){ 2 FTPClient ftpClient = new FTPClient(); 3 try{ 4 String url = properties.getProperty("url"); 5 String username = properties.getProperty("username"); 6 String passwd = properties.getProperty("passwd"); 7 ftpClient.connect(url); 8 ftpClient.login(username, passwd); 9 // ftpClient.setFileType(FTP.BINARY_FILE_TYPE); 10 // ftpClient.setControlEncoding("UTF-8"); 11 12 } catch (Exception e) { 13 // TODO Auto-generated catch block 14 e.printStackTrace(); 15 } 16 return ftpClient; 17 }
=============我风骚的插了进来==================
既然说到Properties,我再贴个读取的工具函数好了,同样很简单,权当是增加篇幅。。
需要注意以下几个事情:
1.那个Tools其实就是个工具类,我把创建的properties文件放在了这个类的目录下面,完事直接取就行了;
2.注意这个函数是private的,我感觉想取这个文件的话还是就在这个工具类里面就好了,也不清楚是不是多此一举(我就这么弄),有其他需求自行修改哈。。
1 private static Properties getProperties() { 2 InputStream inStream = Tools.class.getResourceAsStream("ftpLogin.properties"); 3 Properties properties = new Properties(); 4 try { 5 properties.load(inStream); 6 } catch (IOException e) { 7 // TODO Auto-generated catch block 8 e.printStackTrace(); 9 } 10 return properties; 11 }
=========================================
二、文件上传
heihei,我没使用文件下载,不过看了一下API文档,感觉也就那么回事,有需求再研究下,这里就记一下上传吧。
下面这段代码同样有几个地方需要注意一下:
1. 不要在意那个16行的注释,原来的第三个参数就是fileType,当时脑抽,逻辑是有问题的,放在这里警戒自己;
2. Tools.Mkdirs():这个函数使用来创建多级目录用的,没有在API文档里面找到相关用法,也查了网上的做法,也都不高明。。干脆献丑自己搞了一段,没什么水平,需要改进,后面会贴出来;
3. changeWorkingDirectory(path):创建完了目录需要将当前工作目录切换过来,然后直接在下面创建文件;
4. storeFile(String remote,InputStream local): 名字是要写到的远程文件的名字,这里用了和输入文件同名,关于输入流,就是内容文件(输入文件)的输入流;
5. sendSiteCommand(String command): 这个是在远程端执行ftp命令用的,和一个叫doCommand()的函数有点像,不过那个貌似只能操作有输出的命令,没用过,不负责任哈:-),这个函数还要多说一点,之所以这么用,是因为我发现使用storeFile()上传上去的文件都是默认700的,因为我结合了apache2服务器使用,直接导致劳资无法使用已经上传的文件,所以才有修改权限的事情;
6. 这里其实写的还有点问题,因为我写文件的次数比较多,从这么get然后再close的用法显然在性能上面有损耗,最好在上传的时候集中一下,就是说要先get到这个ftpClient,然后再集中上传,都上传完了再close掉,不过我暂时还没有写。。
7. 关于这一大片的tmp**,这东西我写了不少,但是一个都没用,不过倒是可以留着,等开始调试的时候会发现好处的;
8. 关于中文编码:一共要进行两次转码,根据查询发现FTPClient默认的字符编码貌似是ISO-8859-1,而我使用的是linux平台,应该是UTF-8的,所以这里需要进行一次转码,是对文件名的,还有一次在Mkdirs()中,是对文件夹名的;
1 /* 2 * @param filename : the file u want to upload(without path) 3 * 4 * @param path : the path u want to upload to(without filename) 5 * 6 */ 7 @SuppressWarnings("resource") 8 public static Boolean FtpUpload(File filename,String path){ 9 Boolean success = false; 10 String LOCAL_CHARSET = "GBK"; 11 String SERVER_CHARSET = "ISO-8859-1"; 12 try { 13 FileInputStream in = new FileInputStream(filename); 14 //get properties & get connect ftpClient 15 Properties properties = Tools.getProperties(); 16 FTPClient ftpClient = Tools.getFtpClient(properties); 17 18 // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK). 19 if(FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) { 20 LOCAL_CHARSET = "UTF-8"; 21 } 22 ftpClient.setControlEncoding(LOCAL_CHARSET); 23 24 // if(fileType == FTP.BINARY_FILE_TYPE){ 25 // ftpClient.setFileType(FTP.BINARY_FILE_TYPE); 26 // } 27 //check if connection failed 28 int reply = 0; 29 reply = ftpClient.getReplyCode(); 30 if (!FTPReply.isPositiveCompletion(reply)) { 31 ftpClient.disconnect(); 32 return success; 33 } 34 //mkdirs(in case of multiple level directory) 35 if(path != null && !path.isEmpty()){ 36 //MKdirs中有关于path的转码 37 boolean tmpMkdir = Tools.Mkdirs(ftpClient, path); 38 System.out.println(ftpClient.printWorkingDirectory()); 39 boolean tmpCd = ftpClient.changeWorkingDirectory(path); 40 System.out.println(ftpClient.printWorkingDirectory()); 41 } 42 //上传文件时,文件名称需要做编码转换 43 String name = new String(filename.getName().getBytes(LOCAL_CHARSET),SERVER_CHARSET); 44 boolean tmpStore = ftpClient.storeFile(name, in); 45 boolean tmpDoCommand = ftpClient.sendSiteCommand("chmod 755 " + name); 46 //here is a mark that maybe i should close ftp connections after all store.. 47 //like create a new function to close :-( 48 in.close(); 49 ftpClient.logout(); 50 success = true; 51 }catch (FileNotFoundException e1) { 52 // TODO Auto-generated catch block 53 e1.printStackTrace(); 54 } catch (SocketException e) { 55 // TODO Auto-generated catch block 56 e.printStackTrace(); 57 } catch (IOException e) { 58 // TODO Auto-generated catch block 59 e.printStackTrace(); 60 } catch (Exception e){ 61 e.printStackTrace(); 62 } 63 return success; 64 }
===============我又风骚的插了进来===================
上面说到了那个创建Mkdirs的函数,在这里贴出来(话说真的写的好渣,不过比没有强哈,我会再改进,这篇博文立了好多flag,我会一个一个收的,改进是永无止境的)
下面还是来做些说明:
1. sendSiteCommand(): 这个函数有出现的原因是因为之前的文件上传,我只对文件进行了权限修改,这边是创建文件夹,我也做了同样的事情;
2.创建完成之后,我又把目录弄回去了,考虑到mkdir了之后操作各有不同,就恢复了工作路径,还是那句话,根据需要自行处理;
3. 关于中文编码:上面提到过,一共需要两次转码,之前一次是对文件名进行转码,在这里则对文件夹名进行转码;
1 public static Boolean Mkdirs(FTPClient ftpClient,String path){ 2 Boolean success = false; 3 String[] subDirs = path.split("/"); 4 5 String LOCAL_CHARSET = "GBK"; 6 String SERVER_CHARSET = "ISO-8859-1"; 7 8 //check if is absolute path 9 if(path.substring(0, 0).equalsIgnoreCase("/")){ 10 subDirs[0] = "/" + subDirs[0]; 11 } 12 boolean tmpMkdirs = false; 13 try { 14 // 开启服务器对UTF-8的支持,如果服务器支持就用UTF-8编码,否则就使用本地编码(GBK). 15 if(FTPReply.isPositiveCompletion(ftpClient.sendCommand("OPTS UTF8", "ON"))) { 16 LOCAL_CHARSET = "UTF-8"; 17 } 18 ftpClient.setControlEncoding(LOCAL_CHARSET); 19 20 String orginPath = ftpClient.printWorkingDirectory(); 21 for(String subDir : subDirs){ 22 //encoding 23 String strSubDir = new String(subDir.getBytes(LOCAL_CHARSET),SERVER_CHARSET); 24 tmpMkdirs = ftpClient.makeDirectory(strSubDir); 25 boolean tmpDoCommand = ftpClient.sendSiteCommand("chmod 755 " + strSubDir); 26 ftpClient.changeWorkingDirectory(strSubDir); 27 success = success || tmpMkdirs; 28 } 29 //ftpClient.changeWorkingDirectory(orginPath); 30 } catch (IOException e) { 31 // TODO Auto-generated catch block 32 e.printStackTrace(); 33 } 34 return success; 35 }
==============================================
三、apache2服务器
上面说到了basePath还记得不,由于项目需求,要从搭建的FTP服务器上面读取数据,就干脆弄了个apache服务器,用来将FTP的目录发布出来,供其他项目共享,而这个basePath就是共享目录的目录名,记得加"/"哈。
搭建apache服务器的具体细节请参看我的另一篇博文 Ubuntu下搭建apache服务器。