记一次FTPClient的使用。(复制、删除、移动)
来源:互联网 发布:招聘网站知乎 编辑:程序博客网 时间:2024/06/05 01:45
由于项目有个需求是要去ftp服务器上取文件,文件为xml格式,解析后定时抽取入库。
一开始分了三步,第一步取文件,第二步解析,第三步抽取入库。后来做完后又要根据将文件抽取入库失败的移动到error目录上。所以最后大致上可以分成四步。由于定时器可以利用spring+quartz实现,用到的是spring配置文件,所以就不记录了。
在刚开始动手时,由于没搭建好ftp服务器测试,所以就先做了第二步xml的解析。开始时将解析方法声明为parseXML(File file),将文件放到了本地磁盘上测试,通过。然后开始有点坑的就来了,当搭建好ftp服务器后,就尝试第一步,ftp上取文件。就去调用 commons.net上的FTPClient。
第一步,ftp服务器上取文件。
第一小步,肯定就是连接上ftp服务器,度娘,连接ftp如下:
public void connectServer(String ip, int port, String userName, String userPwd) { ftpClient = new FTPClient(); try { int reply; // 连接 ftpClient.connect(ip, port); // 登录 ftpClient.login(userName, userPwd); reply = ftpClient.getReplyCode(); } catch (Exception e) { log.error("ftp链接失败", e); } }
第二小步就是获取ftp上的文件:
FTPFile[] ftpFIles = ftpClient.listFiles();
第二步:解析xml文件
文件取到了,当然就是去解析xml了,然后坑爹的就来了,之前定好的方法parseXML(File file),接受的是File文件,然而ftpClient获取下来的文件是FtpFile格式。一开始想着怎么去转,花费了一些时间,可能有转过去的方法,但是我没找到。然后度娘上又给了一个是将FtpFile格式的xml解析的,没办法,就换成了 parseXML(FTPFile fPath)。由于SAXReader里的read方法可以接受inputstream流,所以只要将FtpFile转换成流就ok了,ins = ftpClient.retrieveFileStream(fPath.getName());
这里有个坑,就是每次调用ftpClient.retrieveFileStream()后,需要调用ftpClient.getReply()这个方法,把接下来的226消费掉,如果没有写,下一次ftpClient.retrieveFileStream()返回的InputStream对象会一直为null!!!代码如下:
public List<Map<String, Object>> parseXML(FTPFile fPath) { List<Map<String, Object>> parseList = new ArrayList<Map<String, Object>>(); String rtn = ""; Document document = null; InputStream ins = null; try { SAXReader saxreader = new SAXReader(); ins = this.ftpClient.retrieveFileStream(fPath.getName()); document = saxreader.read(ins); Element rootEle = document.getRootElement(); //<dataroot> //得到所有的一级子元素 List firstElements = rootEle.elements(); Iterator it = firstElements.iterator(); while(it.hasNext()){ //依次得到每一个一级子元素 Element firstElement = (Element) it.next(); //<YW_LZXX> //得到一级子元素下面的所有元素,及其附带值 List second_Elements=firstElement.elements(); Iterator second_Element=second_Elements.iterator(); Map<String, Object> map = new HashMap<String, Object>(); while(second_Element.hasNext()){ Element sec_Element=(Element)second_Element.next(); map.put(sec_Element.getName(), sec_Element.getText()); } parseList.add(map); } ftpClient.getReply(); } catch (Exception e) { log.error("xml解析失败", e); } finally { if(ins != null){ try { ins.close(); } catch (IOException e) { log.error("IO关闭失败", e); } } } return parseList; }
第三步就是将xml解析后的数据入库了,这个略。
第四步就是要将入库失败的文件进行移动
这一步是在xml解析入库时做的,循环遍历时,先确定一个xml文件里面总入库的数据有多少,然后成功入库的累加,最后判断成功入库数和总入库数是否相等,当成功数小于入库数时,就将该文件进行复制到errors目录,再将原文件删除。
一开始的思想是先将文件转换成流,判断有没有errors目录,有的话切换目录,然后将数据流转化成文件,再把文件删除。
所以一开始的做法如下:
InputStream ins = ftpClient.retrieveFileStream(ftpFile.getName()); //转换成输入流ftpClient.changeWorkingDirectory("\\errors");//切换目录ftpClient.storeFile(ftpFile.getName(), ins);//复制文件ins.close();ftpClient.deleteFile("/"+ftpFile.getName());//删除文件
坑一:ftpClient.changeWorkingDirectory(String pathname);//切换目录
由于没发现FTPClient有直接判断是否存在某个目录,所以在问度娘的时候,得到了一个巧方法,就是ftpClient.changeWorkingDirectory(“\errors”);根据返回值true或false来判断有没有该目录。为什么说他是坑呢,因为在用的时候,看不到效果,用ftpClient.printWorkingDirectory()看当前的路径时,很多时候发现切换后的路径为null。
坑二:ftpClient.storeFile(String remote, InputStream local);//复制文件
在进行文件复制时,调用该方法后,文件并未复制到指定的目录。没办法,又是度娘,
发现很多都是说未成功是需要设置ftpClient.enterLocalPassiveMode();将其设置为消极模式。具体百度 FTPClient主动模式和被动模式 了解一下区别,但是我发现设置后,还是没用啊,没办法。
坑三:InputStream ins = ftpClient.retrieveFileStream(String remote); //转换成输入流
当进行了该转化后,不管后面的文件有没有复制成功,下次循环时,去解析xml时,得到的inputstream为null,可能就是因为没设置ftpClient.getReply(); 的原因。
因为当时赶着下班要提交,所以第四步的复制移动删除就没做了。没错,别人下班,你还是要加班。作为一个新手,好不容易有点事做了,怎么可以做一半就不做了呢,所以那天晚上加班。
加班嘛,中间隔了一段时间去吃饭,脑子也放空一下,跳出了复制后删除的思维。因为要求是将错误文件移动到指定目录,所以就百度了FTPClient的移动功能,没想到还真有,
ftpClient.rename(String from, String to);//移动文件
至于目录是否存在,直接用ftpClient.makeDirectory(String pathname);//创建目录
存在返回false,不存在就创建。
至此,功能就实现了,但是觉得以后万一遇到复制功能 的呢,所以就又去找度娘一下。并实验了一下,可以用,代码如下:
ftpClient.setBufferSize(1024); ByteArrayOutputStream fos=new ByteArrayOutputStream(); ftpClient.retrieveFile("\\"+ftpFile.getName(), fos); ByteArrayInputStream in=new ByteArrayInputStream(fos.toByteArray()); ftpClient.storeFile("\\"+errorDir+"\\"+ftpFile.getName(), in); fos.close(); in.close();
这是将流在内存里转换,实现了复制功能。
小结:
在实现该功能后,个人感觉有几个常用的FTPClient的方法:
1、ftpClient.retrieveFileStream(String remote);
使用该方法后,调用ftpClient.getReply()方法,否则下次调用该方法会返回null;
2、ftpClient.storeFile(String remote, InputStream local);//复制文件
当使用该方法返回true,但是目录没有成功复制文件时,可以设置一下被动模式ftpClient.enterLocalPassiveMode();
3、ftpClient.changeWorkingDirectory(String pathname);//切换目录
此方法可以切换目录。但是个人在用过程中,对其返回值true和false感觉有点怪。
4、ftpClient.rename(String from, String to); //移动文件到新目录
当只是需要移动文件时,可以选择此方法,不必像楼主刚开始时那样,又是判断有没有目录、复制、删除文件。
5、ftpClient.deleteFile(String pathname); //删除文件
6、ftpClient.makeDirectory(String pathname);//创建目录
7、ftpClient.retrieveFile(String remote, OutputStream local)//移动文件
对于复制文件,如果小文件,直接调用ftpClient.storeFile(String remote, InputStream local);可能可以成功,但是如果文件偏大,可能复制就会出问题。所以找了一个利用将文件读到内存的方法http://bbs.csdn.net/topics/390373219,据说16Mb以上的都能复制成功,虽然没去实验,但是基本的复制确实没问题。
ftpClient.setBufferSize(1024); ByteArrayOutputStream fos=new ByteArrayOutputStream();ftpClient.retrieveFile("\\"+ftpFile.getName(), fos);ByteArrayInputStream in=new ByteArrayInputStream(fos.toByteArray());ftpClient.storeFile("\\"+errorDir+"\\"+ftpFile.getName(), in);fos.close();in.close();
- 记一次FTPClient的使用。(复制、删除、移动)
- FtpClient和FTPClient下载的使用
- ftpClient 的上传下载及删除
- FTPClient的使用类
- 使用ftpclient的细节
- 文件夹的删除、重命名、移动、复制
- 文件的复制、移动与删除
- 文件的复制、删除和移动命令
- 文件夹的删除、重命名、移动、复制
- Ubuntu的复制,删除,移动命令
- 文件的复制删除移动等操作
- vc使用SHFileOperation()的使用对文件夹的复制,删除,移动,重命名
- centos7 复制移动删除
- FTPClient的使用(二) ftp(文件夹和文件)下载,上传,复制备份
- apache的ftpclient的使用
- apache的ftpclient的使用
- FTPClient的简要使用示例
- Vim编辑器如何使用复制、移动及删除操作
- 获取window值
- Shop项目--6. 商品的浏览历史记录。product.list.jsp
- 本地部署ngrok
- C++高级工程(六)预处理器
- 直接算法+动态规划算法求解最大子段和
- 记一次FTPClient的使用。(复制、删除、移动)
- 大端与小端
- C++之最长公共子序列(21)---《那些奇怪的算法》
- python学习总结---if-else判断
- eleme接口测试01(C#)
- python判断自身是否正在运行
- SSH整合
- Mysql系列课程--第五章 高级查询 表连接 子查询 case when
- CCF-训练50题-NO.29-最少钱币数